# Arduino 课程

## 资料下载

- [项目代码](./项目代码.7z)

- [Arduino库文件](./Arduino库文件.7z)

- [APP](./APP.7z)

## Arduino软件

1.安装Windows驱动程序

![](media/6cf6312dc7c7db27794b54d58a8bf80c.png)

### 1.1 下载安装arduino软件：

先进入arduino官方的网站：[<u>https://www.arduino.cc/</u>](https://www.arduino.cc/)
，下载最新版本的arduino软件。进入网站之后会看到以下这个界面，如下图所示：

![](media/a9dc31a75935a43609331a95a8874604.png)

需要下载的arduino软件就在SOFTWARE这个栏目里面

![](media/51887b33da18ab33c49ad96337f9667b.png)

点击DOWNLOADS，就会出现最新版本的ARDUINO软件，Arduino
软件有很多版本，有Windows、Mac、Linux系统的，如下图所示：![](media/37c29f5bb1f0b883dda2de13aac92d10.png)

选择要下载的版本，可以下载最新的版本。

![](media/36245a9e4e95686e695f1ca3d4916bb7.png)

或者，你也可以选择旧版本，如下图所示。但在这个项目中，我们选择使用的是1.8.13版本。

![](media/dc0788cdd1378769fd03c7db93431517.png)

点击Windows Win7and newer下载Arduino 1.8.13版本的安装程序，需要手动安装。而当你点击Windows ZIP file时，Arduino 1.8.13版本的zip文件将被直接下载，您只需要解压缩它就可以完成安装。

![](media/e321a129ef60a8367c31aaecca3cf92b.png)

一般情况下，点击JUST DOWNLOAD就可以下载了，当然如果你愿意，你可以选择小小的赞助，以帮助伟大的Arduino开源事业。

下载完成后，继续安装，当你收到操作系统的警告时，请允许驱动程序安装。选择好要安装的组件后点击“下一步”。

![](media/f2135470a67dbdafe430c9ffaab25504.png)

选择安装目录(我们建议保持默认目录)，然后点击“安装”。

![](media/9bfce902c246b9b2dcdba137c8d08a98.png)

如果出现以下界面，则应选择Install.

![](media/85b29de2aa791ecc77280ccde91e53c5.png)

该过程将提取并安装所有必需的文件，以正确执行Arduino软件(IDE)。

![](media/9731a7136148c36cea205ac2f7a27b9d.png)

### 1.2 在Windows系统上安装驱动：（注意：如果电脑已经安装了驱动程序，则不需要再安装驱动；如果没有，则需要进行以下操作）

在使用Keyes Uno Plus控制板之前，必须安装它的驱动程序，否则它将无法与计算机通信。与Arduino UNO R3的USB系列芯片(ATMEGA8U2)不同，Keyes Uno Plus控制板采用了USB系列芯片CP2102芯片和USB C型接口。在Arduino IDE 1.8.0版本及以上版本中包含了CP2102芯片的驱动程序。通常，您将控制板通过USB线连接到计算机，电脑就会识别到硬件，WINDOWS系统就会自动安装CP2102的驱动。过一会儿，CP2102
驱动就会安装成功。

![](media/fe2c97ce022a5a071b2530328df74008.png)

注意:

1.  请确保您的IDE更新到1.8.0或更新版本；

2.  如果您下载的Arduino     IDE版本低于1.8，请下载CP2102的驱动程序并手动安装。

    下载CP2102驱动程序的链接：[https://fs.keyestudio.com/CP2102-WIN](https://fs.keyestudio.com/CP2102-WIN)

如果驱动安装失败，则需要手动安装驱动。请打开计算机的设备管理器，右键单击“计算机”-----单击“属性”-----单击“设备管理器”。在端口
(COM &LPT)或其他设备。黄色感叹号表示CP2102驱动程序安装失败。

![](media/68047c23361ba2d818bc5bd3b282c8f3.png)

显示CP2102的驱动没有安装成功，有一个黄色的感叹号。我们可以双击硬件更新驱动

![](media/d7ff57523c843e28aa6369502b87706c.png)

点击“浏览我的电脑以查找驱动程序”，找到我们安装或者下载的Arduino软件.

![](media/c0823278d69e7777bf0ccd7673f6dd3c.png)

在Arduino软件文件夹（![](media/04370f5de2f4ebd52526b0453d7cc410.png)）里面有一个drivers文件夹，打开drivers文件夹就可以看到CP210X系列芯片的驱动。

点击“浏览(R)...”,选中CP210X系列芯片的驱动，点击“下一步”。

![](media/13c29715015d445c22d357b8c82e26dc.png)

过一会儿，驱动安装成功。

![](media/0b5b54ab06d573af9a0e21cd8d9a6b2d.png)

这个时候再打开计算机设备管理器，你就可以看到CP2102的驱动程序已经安装成功了，刚刚的那个黄色的感叹号不见了。

![](media/8747a91c8ec8a9b1243fd8b5b1634edb.png)

### 1.3 配置Arduino IDE

驱动程序安装成功后，是时候配置IDE使用相同的设备和端口开始上传代码。对于Keyestudio Uno PLUS控制板，我们应该转到工具→开发板；然后选择Arduino Uno(如下图所示)。![](media/0984cf4e0f61c879ad4989db292b5630.png)

然后选择正确的COM端口。驱动安装成功后，可以看到对应的COM口。![](media/8747a91c8ec8a9b1243fd8b5b1634edb.png)

转到工具→端口→COM3

![](media/69e50e96173623746d4f871ec5dc0734.png)

将代码程序上传到控制板之前，必须演示Arduino IDE工具栏中出现的每个符号的功能。

![](media/3ebf70bd0365899bd8b7045e0d283de7.png)

A - 用于检查是否存在任何编译错误。

B - 用于将程序上传到Arduino控制板（Keyestudio Uno PLUS控制板）。

C - 用于创建新草图的快捷方式。

D - 用于直接打开示例草图之一。

E - 用于保存草图。

F - 用于从板接收串行数据并将串行数据发送到板的串行监视器。

### 1.4 开始第一个程序

打开“文件”选择“示例”，从“Basics”中选择“Blink”，如下图所示：

![](media/1153486533826614ed23b5de7056b921.png)

当草图窗口打开时，您可以在窗口中看到整个草图。

![](media/db1a10c7351115be461bda8027197c26.png)

设置板型和COM口，对应的板型和COM口显示在IDE的右下角。

![](media/b97838765e669a56d231308b5e89d5bf.png)

点击![](media/ddd21c81338ae1f6b7f84de2a3caecf0.png)开始编译程序，检查错误。

![](media/0ff8434eb11425db95c445f6dab255f5.png)

点击![](media/9c9158a5d49baa740ea2f0048f655017.png)上传程序，几秒种后，上传成功。

![](media/97f9b92bd203aa7d8cc0f8c11aa905c8.png)

上传程序成功，利用USB线上电，Keyestudio Uno PLUS控制板上的板载LED灯点亮1秒，熄灭1秒，循环进行。恭喜你，完成了第一个项目。

2.在MAC 系统上安装驱动

![](media/a6fc83596009c574d8e29ef383748549.png)

### 2.1下载安装arduino软件：

![](media/5ee4ad51b656bec9288bb255e9f0c531.png)

### 2.2下载CP2102的驱动程序： 

相关下载链接：[<u>https://fs.keyestudio.com/CP2102-MAC</u>](https://fs.keyestudio.com/CP2102-MAC)

### 2.3如何安装CP2102驱动程序：（注意：如果已经安装了驱动程序，则不需要再安装驱动；如果没有，则需要进行以下操作） 

（1）用USB线将Keyestudio Uno PLUS控制板连接到你的MacOS系统电脑上，并打开Arduino IDE。

![](media/a72fe5a29c6af0cd24aba7ab59b4996e.png)

点击工具，选择主板:“Arduino Uno”，串口为
/dev/cu.usbserial-0001。

![](media/ef09ff03db61fbb040a09f798fcabb44.png)

点击![](media/9c9158a5d49baa740ea2f0048f655017.png)上传代码，如果上传成功，如下图所示，会显示上传成功。

![](media/f76eccfb96fb58aee8e7ecba1621be28.png)

注意：如果上传代码程序成功了，则不需要再安装驱动，可以跳过下面安装驱动的步骤；如果没有上传成功，则需要跟着下面的步骤安装CP2102驱动。执行下面（2）~（13）步。

（2）CP2102驱动下载链接：

<https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>

（3）点击下载MacOS 版本。

![](media/c09e7c279a858574756d1192b3a995aa.png)

（4）解压下载好的压缩包。

![](media/6870a714ddd11015dc43b1d5743e0666.jpeg)

（5）打开文件夹，双击SiLabsUSBDriverDisk.dmg文件。

![](media/570d0452818519cf67c7ac750032a901.png)

可以看到以下文件。

![](media/3f1afe9499f6d852492cfb9d6b11e9ab.jpeg)

（6）双击 Install CP210x VCP Driver 等待界面。

（7）点击Continue

![](media/b1cb125dccf6470ebe255f8f65b902eb.jpeg)

（8）先点击Agree ，然后点击Continue

![](media/865dcc76cb7f58854b56f1020233f05e.jpeg)

（9）继续点击Continue ，然后输入你的用户密码

![](media/1ef6d65b61ad7c6e0a3989ba59de74d5.jpeg)

![](media/1204fca49aac9a5a2560f1865d59ca56.png)

（10）回到安装界面，根据提示等待安装

![](media/0da6d0d4296d6e3de0b30dfd3c615265.jpeg)

（11）安装成功

![](media/7cca827fe946096f228797dadce10661.jpeg)

（12）打开arduinoIDE，点击工具，选择主板为Arduino Uno
，串口为/dev/cu.usbserial-0001

![](media/fb321aedfdd0e621f041b974e1d44d93.png)

（13）然后再点击上传程序，就可以看到烧录成功。

![](media/f76eccfb96fb58aee8e7ecba1621be28.png)

3. 将库文件安装到Arduino

什么是库?

库是一组代码，可以方便地连接到传感器、显示器、模块等。例如，内置的LiquidCrystal库可以帮助与LCD显示器对话。Internet上还有数百个额外的库可供下载。参考资料(https://www.arduino.cc/en/Reference/Libraries)中列出了内置库和其中一些附加库。

如何安装库？

### 3.1 手动安装 

当你想手动添加一个库时，首先要退出Arduino应用程序，然后需要将库作为ZIP文件下载，将其展开并放入适当的目录中。

以下载安装IR Remote库为例，我们进入链接：[<u>https://github.com/shirriff/Arduino-IRremote</u>](https://github.com/shirriff/Arduino-IRremote)，在GitHub上找到IR Remote库。首先，通过点击绿色Code按钮，然后点击download ZIP，这样就可以下载IR Remote的ZIP库

![](media/bb6556490a4d698496daa663fb73dca2.png)

下载后，解压缩IRremote库文件包。

![](media/888062840a4315aa5786890a6a580744.png)

然后将IRremote库文件包移到Arduino文件夹。

![](media/a67a5b5921be4dbc1b0ce92627111d15.png)

打开Arduino文件夹中的libraries文件夹，然后将Arduino- irremote
-master文件夹拖动到Libraries文件夹中。

![](media/b9ddb20f044a39b304fffed86213908c.png)

个人建议：（使用我们提供的库文件，省得去下载）

使用我们提供的库文件，将每一个库文件压缩包依次解压后分别复制粘贴到软件Arduino文件夹中的libraries文件夹中去。

![](media/5a32d608e802c86ded10a3f366ba4808.png)

例如：以![](media/a9ed380289a225adc9d8f7f25199ba90.png)为例，右键点击![](media/a9ed380289a225adc9d8f7f25199ba90.png)，选择点击“解压文件（A）...”

![](media/02a3bee21dbe6261885a0abdbd537980.png)

将解压后的文件包![](media/aeaef0e27d77db862d4ac210285bcfa4.png)复制粘贴到软件Arduino文件夹中的libraries文件夹中去。

![](media/cfb4ead2a06cfc3e06b2a318614d4d2c.png)

依次类推，采用上述方法将剩下的库文件解压后都复制粘贴到软件Arduino文件夹中的libraries文件夹中去。

![](media/4ddfa3abfdb555f402ef91e1b7a5e0ab.png)

这样，库文件就手动添加完成了。

## 项目

### 项目1: Hello World

1.项目介绍：

对于Arduino初学者，我们将从一些简单的东西开始。在这个项目中，您只需要一个Plus开发板和USB电缆来完成“Hello World!”项目。它不仅是Arduino板和PC的通信测试，也是Arduino世界的初级项目。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|
|Keyes Uno Plus 开发板*1|USB 线*1|

3.项目接线：

![](media/480b59c1830ea74c657938f1c8a17606.png)

4.项目代码：

将使用一个简单的If()语句编程控制结构。Arduino使用串行监视器来显示打印语句、传感器数据等信息。这是一个非常强大的工具，用于调试长代码。现在是你的第一个代码：

```c
char val;//定义变量val.

void setup()
{
	Serial.begin(9600);// 设置波特率为9600. 
}

void loop()
{
  if (Serial.available() > 0) 
  {
    val=Serial.read();// 读取指令或字符从PC到Arduino，并赋值给val.
    if(val=='R')// 确定接收的指令或字符是否为“R”.
    {  // if it’s “R”,    
     Serial.println("Hello World!");// 显示“Hello World !”字符串.
    }
  }
}
```

5.项目结果：

选择正确的Arduino IDE主板类型和COM端口，点击Arduino IDE上的上传按钮上传代码。上传成功后，单击![](media/2f6bca56f724e45a855335cb53ae9b4e.png)图标进入串行显示器。

![](media/9a18ce910763487ce4d8c3878ac2aca7.png)

每当你在文本框中输入一个“R”，并单击“发送”，串行监视器将显示一个“Hello World！”。

![](media/a39e7400f28a5bb14dd5ef580a1300dd.png)

### 项目2: LED闪烁 

1.  项目介绍：

在这个项目中，我们将向你展示LED闪烁效果。我们使用Arduino的数字引脚打开LED，让它闪烁。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/7eb361d680dfa351f07f8527aeb37abd.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|红色LED*1|220Ω电阻*1|面包板*1|跳线*2|USB 线*1|

3.元件知识：

（1）LED:

![Img](./media/img-20250317153510.png)

LED是一种被称为“发光二极管”的半导体，是一种由半导体材料(硅、硒、锗等)制成的电子器件。它有正极和负极。短腿为负极，接GND，长腿为正极，接5V.

![](media/14a84d5f016d7566151a5563c502787e.png)

（2）五色环电阻

电阻是电路中限制或调节电流流动的电子元件。左边是电阻器的外观，右边是电阻在电路中表示的符号。电阻(R)的单位为欧姆(Ω)，1 mΩ= 1000 kΩ，1kΩ= 1000Ω。

![](media/8a86f65cf820d08e8956daa70d1c4195.jpeg)
![](media/f6079fe22518f0fc1b0c3a3b93a516a1.png)

我们可以使用电阻来保护敏感组件，如LED。电阻的强度（以Ω为单位）用小色环标记在电阻器的主体上。每种颜色代表一个数字，你可以用电阻对照卡查找。

\-色带1 – 1st Digit.

-色带 2 – 2nd Digit.

-色带 3 – 3rd Digit.

-色带 4 – Multiplier.

-色带 5 – Tolerance.

![](media/c3df005312cd9f6d4cdae6abf3cddb83.png)

在这个套件中，我们提供了8个具有不同电阻值的五色环电阻。这里以3个五色环电阻为例：

220Ω 电阻\*10

![](media/793740d0b936e516ca354111e2d0eb79.png)

10KΩ 电阻\*10

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

1KΩ 电阻\*10

![](media/8088ed382616afb346d44f5aacfb52d1.png)

在相同的电压下，会有更小的电流和更大的电阻。电流、电压、电阻之间的联系可以用公式表示：I=U/R。在下图中，目前通过R1的电流:
I = U / R = 3 V / 10 KΩ= 0.0003A= 0.3mA。

![](media/b3eec552e4dfad361833730698621776.png)

不要把电阻值很低的电阻直接连接在电源两极，这样会使电流过高而损坏电子元件。电阻是没有正负极之分。

（3）面包板

面包板是实验室中用于搭接电路的重要工具。面包板上有许多孔，可以插入集成电路和电阻等电路元件。熟练掌握面包板的使用方法是提高实验效率，减少实验故障出现几率的重要基础之一。下面就面包板的结构和使用方法做简单介绍。一个典型的面包板如下所示：

![](media/837cd6ec4b1b09cc46340201a6425958.jpeg)

面包板的外观和内部结构如上图所示，常见的最小单元面包板分上、中、下三部分，上面和下面部分一般是由一行或两行的插孔构成的窄条，中间部分是由中间一条隔离凹槽和上下各5
行的插孔构成的条。

![](media/099510035abc223273495e042a7bd6b6.jpeg)

在面包板的两个窄条分别有两行插孔，两行之间是不连通的，一般是作为电源引入的通路。上方第一行标有“+”的一行有10组插孔（内部都是连通），均为正极；上方第二行标有“-”的一行有10组插孔，（内部都是连通），均为接地。面包板下方的第一行与第二行结构同上。如需用到整个面包板，通常将“+”与“+”用导线连接起来，“-”与“-”用导线连接起来。

　　中间部分宽条是由中间一条隔离凹槽和上下各5
行的插孔构成。在同一列中的5
个插孔是互相连通的，列和列之间以及凹槽上下部分则是不连通的。外观及结构如下图：

![](media/3fc9a04d9354e63ca0e89eb7ed627128.png)

中间部分宽条的连接孔分为上下两部分，是面包板的主工作区，用来插接原件和跳线。在同一列中的5个插孔（即a-b-c-d-e，f-g-h-i-j）是互相连通的；列和列之间以及凹槽上下部分是不连通的。在做实验的时候，通常是使用两窄一宽组成的小单元，在宽条部分搭接电路的主体部分，上面的窄条取一行做电源，下面的窄条取一行做接地。中间宽条用于连接电路，由于凹槽上下是不连通的，所以集成块一般跨插在凹槽上。

4.项目电路图和接线图：

请看项目电路图和接线图，这里我们使用数字引脚10，并将一个LED连接到一个220欧姆电阻，以避免大电流损坏LED。

![](media/50ee6c9b34f9b9017aeb430f1785c233.png)

电路图

![](media/67dd9f24c960a58c895b9d6aa62f1a9c.png)

接线图

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

5.项目代码：

```c
int ledPin = 10; // 定义数字引脚10.

void setup()
{
	pinMode(ledPin, OUTPUT);// 定义led引脚为输出.
}

void loop()
{
    digitalWrite(ledPin, HIGH); // 点亮LED.
    delay(1000); // 等待1秒.
    digitalWrite(ledPin, LOW); // 熄灭LED.
    delay(1000); // 等待1秒
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，连接控制板D10引脚的LED灯每秒亮/灭一次。

7.代码说明:

pinMode(ledPin，OUTPUT) -
在使用Arduino的引脚之前，你需要告诉控制板它是INPUT还是OUTPUT。我们使用一个内置的“函数”pinMode()来做到这一点。

digitalWrite(ledPin，HIGH) -
当使用引脚作为OUTPUT时，可以将其命令为HIGH（输出5伏）或LOW（输出0伏）。

### 项目3: 呼吸灯

1.项目介绍：

在这个项目中，我们将学习ARDUINO的PWM控制。PWM是脉宽调制(Pulse Width Modulation)的缩写，是一种将模拟信号电平编码为数字信号电平的技术。这里，我们使用PWM来控制LED从亮→暗，循环进行。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/3ec5906fad2172708d449390140f55e6.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|红色LED*1|220Ω电阻*1|面包板*1|跳线*2|USB 线*1|

3.元件知识：

![](media/6549bdbfd4e7b6b2b341012105d655e8.png)

脉宽调制的工作原理：PWM是脉冲宽度调制(Pulse Width Modulation)的缩写，它是一种控制LED的亮度、直流电机和伺服电机的速度的技术。Arduino数字引脚要么产生5V(当变成高)或0V(当变成低)。然而，PWM输出的是方波信号。因此，如果我们想让LED变暗，我们不能从数字引脚获得0到5V之间的电压，但我们可以改变信号的ON（开）和OFF（关）时间。如果我们将改变开和关时间足够快，那么led的亮度将改变。在进一步讨论之前，让我们讨论一些与PWM相关的术语。

ON (On Time)：信号高的时候。

OFF (Off Time)：信号低的时候。

周期：它是On Time和Off Time的总和。

占空比：信号在某一时间段内处于高水平时，占时间的百分比。

所以在50%占空比和1Hz频率下，led会点亮半秒时间，另一半时间熄灭。如果我们将频率增加到50Hz(每秒50次ON和OFF)，那么led将被人眼看到以一半的亮度在发光。![](media/378b4b5fa569bde8a54b972b1df3ee65.jpeg)

Arduino 与 PWM

Arduino IDE有一个内置的函数analogWrite()，可以用来产生PWM信号。大多数引脚产生的信号频率约为490Hz，我们可以使用这个函数给出0-255的值。

analogWrite(0)表示占空比为0%的信号。analogWrite(127)表示占空比为50%的信号。analogWrite(255)表示100%占空比的信号。在KEYES Uno Plus控制板上，PWM引脚为3、5、6、9、10和11。PWM管脚用~符号标记。在这个项目中，您将学习如何从Plus控制板的数字引脚获得PWM输出和通过代码控制LED的亮度。

4.项目电路图和接线图：

![](media/0ba41877b88d1155d88d20e9209e6741.png)

![](media/6eb4ffe774d259633416a49cdb3199b5.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

5.项目代码：

```c
int ledPin = 6;

void setup()
{
	pinMode(ledPin,OUTPUT);
}

void loop()
{
    for (int value = 0 ; value < 255; value=value+1)
    {
        analogWrite(ledPin, value);
        delay(5);
    }
    for (int value = 255; value >0; value=value-1)
    {
        analogWrite(ledPin, value);
        delay(5);	
    } 
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，你会看到LED灯逐渐亮起来，然后逐渐变暗。循环进行！

7.代码说明:

当我们需要重复执行某句话时，我们可以使用for语句。

for语句格式如下：

![](media/898b748c8052234952c0b893696d952e.png)

for循环顺序如下：

第一轮：1 → 2 → 3 → 4

第二轮：2 → 3 → 4

…

直到2不成立，for循环结束。

知道了这么个顺序之后，回到代码中：

for (int value = 0; value \< 255; value=value+1){

...}

for (int value = 255; value \>0; value=value-1){

...}

这两个for语句实现了value的值不断由0增加到255，随之在从255减到0，在增加到255……，无限循环下去。

再看下for里面，涉及一个新函数analogWrite()。

我们知道数字口只有0和1两个状态，那如何发送一个模拟值到一个数字引脚呢？就要用到该函数。观察一下Arduino板，查看数字引脚，你会发现其中6个引脚旁标有“~”，这些引脚不同于其他引脚，它们可以输出PWM信号。

函数格式如下：

analogWrite(pin,value)

analogWrite()函数用于给PWM口写入一个0~255的模拟值。所以，value是在0~255之间的值。特别注意的是，analogWrite()函数只能写入具有PWM功能的数字引脚，也就是3，5，6，9，10，11引脚。

### 项目4: 交通灯

1.项目介绍：

交通灯在我们的日常生活中很普遍。根据一定的时间规律，交通灯是由红、黄、绿三种颜色组成的。每个人都应该遵守交通规则，这可以避免许多交通事故。在这个项目中，我们将使用一个plus控制板和一些led(红，黄，绿)来模拟交通灯。

2.项目元件：

| Keyes Uno Plus 控制板*1                         | 红色LED*1                                       | 黄色 LED*1                                      | 绿色LED*1                                       |
| ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- |
| ![](media/544243270a027fc8cfa58e0f651a7bf4.png) | ![](media/afa6edd3ff90b027a6f43995a6fb15a2.png) | ![](media/0c1b0f91b4e56bcbc235d06b48809ac9.png) | ![](media/6c688493b558ed5f3e90e7dab38cbd93.png) |
| USB 线*1                                        | 220Ω电阻*3                                      | 面包板*1                                        | 跳线若干                                        |
| ![](media/755ba492c38e44d91e8b2c120dc64904.png) | ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png) | ![](media/e380dd26e4825be9a768973802a55fe6.png) | ![](media/e9a8d050105397bb183512fb4ffdd2f6.png) |

3.项目电路图和接线图：

![](media/58038f5ab1eeddf23f203246df192fd8.png)

![](media/5054b30ebaf5b90a0adebdbf13ee4771.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

4.项目代码：

由于是模拟交通灯，所以每个LED的闪烁时间应该与交通灯系统中的闪烁时间相同。在这个程序中，我们使用Arduino delay()函数来控制延迟时间。

```c
int redled =10; // 初始化数字管脚10.
int yellowled =7; // 初始化数字管脚7.
int greenled =4; // 初始化数字管脚4.

void setup()
{
    pinMode(redled, OUTPUT);// 将红色LED引脚设置为“output”
    pinMode(yellowled, OUTPUT); // 将黄色LED引脚设置为“output”
    pinMode(greenled, OUTPUT); // 将蓝色LED的引脚设置为“output”
}

void loop()
{
    digitalWrite(greenled, HIGH);// 点亮绿色LED
    delay(5000);// 延时5秒
    digitalWrite(greenled, LOW); // 熄灭绿色LED
    for(int i=0;i<3;i++)// 闪烁3次
    {
        delay(500);// 延时0.5秒
        digitalWrite(yellowled, HIGH);//点亮黄色LED
        delay(500);// 延时0.5秒
        digitalWrite(yellowled, LOW);// 熄灭黄色LED
    }
    delay(500);// 延时0.5秒
    digitalWrite(redled, HIGH);// 点亮红色LED
    delay(5000);// 延时5秒
    digitalWrite(redled, LOW);// 熄灭红色LED
}
```

5.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，你看到的现象是：1.首先，绿灯会亮5秒，然后熄灭。2.其次，黄灯会闪烁3次，然后熄灭。3.然后，红灯会亮5秒，然后熄灭。4.继续运行上述1-3个步骤，直到切断plus控制板的电源。

### 项目5: RGB LED

1.项目介绍：

![](media/94bdff69e438989d8e0934e57f2e5c00.png)

RGB led由三种颜色(红、绿、蓝)组成，通过混合这三种基本颜色可以发出不同的颜色。在这个项目中，我们将向你介绍RGB LED，并向你展示如何使用Plus控制板控制RGB LED发出不同的颜色光。即使RGB LED是非常基本的，但这也是一个介绍自己或他人到电子和编码基础的伟大方式。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/f1a86fc81ab4b043263ce7e01e14d470.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|RGB LED*1|220Ω电阻*3|面包板*1|跳线若干|USB 线*1|

3. 元件知识：

显示器大多遵循RGB颜色标准，电脑屏幕上的所有颜色都是由红、绿、蓝三种颜色以不同比例混合而成。

![](media/32abd117bdfbba2f79a0e156048b9d22.png)![](media/5a0792145e8a7d9038bf9de389d75fc6.png)

这个RGB LED有4个引脚，每个颜色(红，绿，蓝)和一个共同的阴极。为了改变RGB led的亮度，我们可以使用Arduino的PWM引脚。PWM引脚会给RGB led不同占空比的信号以获得不同的颜色。

4.  项目电路图和接线图：

![](media/a0f458005a6a670b962be532f10e1f95.png)

![](media/c6d61a1e9b845a1ac67452520d78dab3.png)

注意：

RGB LED最长引脚(共阴极)连接GND。

![](media/1584356c63bf99934ae0810ee02dced3.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

5.项目代码：

```c
int redpin = 11; //选择红色LED的引脚
int bluepin =9; // 选择蓝色LED的引脚
int greenpin =10;// 选择绿色LED的引脚
int val;

void setup() 
{
    pinMode(redpin, OUTPUT);
    pinMode(bluepin, OUTPUT);
    pinMode(greenpin, OUTPUT);
}

void loop()
{
    for(val=255; val>0; val--)
    {
        analogWrite(11, val);
        analogWrite(10, 255-val);
        analogWrite(9, 128-val);
        delay(1);
    }

    for(val=0; val<255; val++)
    {
        analogWrite(11, val);
        analogWrite(10, 255-val);
        analogWrite(9, 128-val);
        delay(1);
    }
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，等几秒钟，你会看到一个彩色的LED。

### 项目6: 流水灯

1.项目介绍：

在日常生活中，我们可以看到许多由不同颜色的led组成的广告牌。他们不断地改变灯光来吸引顾客的注意。在这个项目中，我们将使用Plus
控制板5个led实现流水的效果。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/3ec5906fad2172708d449390140f55e6.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|红色LED*5|220Ω电阻*5|面包板*1|跳线若干|USB 线*1|

3.项目电路图和接线图:

![](media/7f1ce8e3af1e0bb0ce48dc953c744dc9.png)

![](media/4179e6d74ed6851384c593057213932e.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

4.项目代码：

```c
int BASE = 2 ;// 第一个LED的I/O引脚
int NUM = 5; // LED 数量

void setup()
{
    for (int i = BASE; i < BASE + NUM; i ++)
    {
    	pinMode(i, OUTPUT); // 设置I/O引脚为输出
    }
}

void loop()
{
    for (int i = BASE; i < BASE + NUM; i ++)
    {
        digitalWrite(i, LOW); // 设I/O引脚为低电平，依次熄灭led灯。
        delay(200); // 延时
    }
    for (int i = BASE; i < BASE + NUM; i ++)
    {
        digitalWrite(i, HIGH); // 设置I/O引脚为高，依次点亮led灯
        delay(200); // 延时
    }
}
```

5.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，连接开发板D2-D6引脚的5个led会逐渐亮起来，然后逐渐熄灭，就像电池充电一样。

### 项目7: 有源蜂鸣器

1.项目介绍：

有源蜂鸣器是一个发声组件。它被广泛用作电脑、打印机、报警器、电子玩具、电话、计时器等的发声元件。它有一个内在的振动源。只需连接5V电源，即可持续发出嗡嗡声。在这个项目中，我们将使用一个Plus控制板控制有源蜂鸣器发出嗡嗡声。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/4b4f653a76a82a3b413855493cc58fba.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|有源蜂鸣器*1|面包板*1|跳线若干|USB 线*1|

3. 元件知识：

![](media/11ec5ddc982db9928341e858aab94652.png)

有源蜂鸣器内部有一个简单的振荡器电路，可以将恒定的直流电转换成特定频率的脉冲信号。一旦有源蜂鸣器收到一个高电平，它将产生声音。而无源蜂鸣器是一种内部没有振动源的集成电子蜂鸣器，它必须由2K-5K方波驱动，而不是直流信号。这两个蜂鸣器的外观非常相似，但是一个带有绿色电路板的蜂鸣器是无源蜂鸣器，而另一个带有黑色胶带的是有源蜂鸣器。无源蜂鸣器不能区分正极性而有源极性蜂鸣器是可以。如下所示：

![](media/76d53f3b35afaa98712e855302e44e32.png)

4.  项目电路图和接线图：

    ![](media/fa035bc382562639e70dfe26e37502ae.png)

![](media/b4cfef41c91a5c66523b12dd6b452526.png)

注意：有源蜂鸣器正极(“+”/长引脚)接引脚8，负极（短引脚）接GND。

5.项目代码：

```c
int buzzerPin = 8;

void setup ()
{
	pinMode (buzzerPin, OUTPUT);
}

void loop ()
{
    digitalWrite (buzzerPin, HIGH);
    delay (500);
    digitalWrite (buzzerPin, LOW);
    delay (500);
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，有源蜂鸣器发出嗡嗡声。

### 项目8: 无源蜂鸣器

1.项目介绍

在之前的项目中，我们研究了有源蜂鸣器，它只能发出一种声音，可能会让你觉得很单调。这个项目将学习另一种蜂鸣器，被动蜂鸣器。与主动蜂鸣器不同，无源蜂鸣器可以发出不同频率的声音。在这个项目中，你将使用Plus
控制板控制无源蜂鸣器演奏一首歌曲。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/d1ea1bb2b2749820cab389d5b85b838b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|无源蜂鸣器*1|面包板*1|跳线若干|USB 线*1|

3. 元件知识：

![](media/8d0020e53824072cbe9d4f7d2f8acb4f.png)

无源蜂鸣器是一种内部没有振动源的集成电子蜂鸣器。它必须由2K-5K方波驱动，而不是直流信号。这两个蜂鸣器的外观非常相似，但是一个带有绿色电路板的蜂鸣器是无源蜂鸣器，而另一个带有黑色胶带的是有源蜂鸣器。无源蜂鸣器不能区分正极性而有源极性蜂鸣器是可以。

![](media/fc42c5ed014609ff0b290ee5361bb2fd.png)

4.项目电路图和接线图:

![](media/316daae31dbaf7e5efc16a4e89bccbfa.png)

![](media/d333e0d0f94cb954d28e87fd59725e7c.png)

5.项目代码：

```c
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST 0
int tempo=114; // 改变这个可使歌曲变慢或变快
int buzzer = 8;// 将此更改为你想使用的任何一个引脚
// 乐曲的音符后面跟着持续时间.
// A 4表示四分音符，8表示十八分音符，16表示十六分音符，以此类推
// !!负数用来表示带点的注释
// 所以-4意味着一个带点的四分音符，也就是说，四分之一加上十八分之一
int melody[] = {
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//1
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_E4,-4, NOTE_D4,8,  NOTE_D4,2,
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//4
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2,
  NOTE_D4,4,  NOTE_D4,4,  NOTE_E4,4,  NOTE_C4,4,//8
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_C4,4,
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_D4,4,
  NOTE_C4,4,  NOTE_D4,4,  NOTE_G3,2,
  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//12
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2
};
// 给出字节数的类型，每个int值由两个字节(16位)组成
// 每个音符有两个值(音高和持续时间)，所以每个音符有四个字节
int notes=sizeof(melody)/sizeof(melody[0])/2; 
// 这计算了整个音符的持续时间，单位是ms (60s/节拍)*4拍
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
void setup() {
  // 重复旋律的音符
  // 记住，数组是音符数的两倍(音符+持续时间)
  for (int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2) {
    // 计算每个音的持续时间
    divider = melody[thisNote + 1];
    if (divider > 0) {
    noteDuration = (wholenote) / divider; // 常规提示，继续
    } else if (divider < 0) {
      // 虚线注释的持续时间为负
      noteDuration = (wholenote) / abs(divider);
      noteDuration *= 1.5; // 给打点音符增加一半的持续时间
    }
    // 只在90%的时间里演奏这个音符，留下10%作为暂停
    tone(buzzer, melody[thisNote], noteDuration*0.9);
  // 等待特定的时间后再演奏下一个音符.
    delay(noteDuration);
    noTone(buzzer);  // 下一个音节前停止波形产生前的下一个说明.
  }
}
void loop() 
{
//如果你想永远重复这首歌，在这里复制粘贴setup()中的代码.
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，无源蜂鸣器演奏一首歌曲。

### 项目9: 74HC595N控制7个LED 

1.项目介绍：

在之前的项目中，我们已经学过了怎样点亮一个LED。

Plus控制板上只有22个IO端口。我们如何点亮大量的led呢?有时可能会耗尽Arduino板上的引脚，这时候需要用移位寄存器扩展它。你可以使用74HC595N芯片一次控制8个输出，而只占用你的微控制器上的几个引脚。你还可以将多个寄存器链接在一起，以进一步扩展输出。在这个项目中，我们将使用Plus控制板和74HC595N控制7个红色亮灭变化的效果。

2.项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/3ec5906fad2172708d449390140f55e6.png)|![](media/f97e58ab51ec0a274ff3e72e08a7d55d.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|-|
|Keyes UnoPlus控制板*1|红色LED*7|74HC595N芯片*1|220Ω电阻*7|面包板*1|跳线若干|USB 线*1|

3.  元件知识：

![](media/2d97fce31da5f5c35c22358e7c07dd67.png)

74HC595N芯片：简单来说就是具有8
位移位寄存器和一个存储器，以及三态输出功能。移位寄存器和存储器同步于不同的时钟，数据在移位寄存器时钟SCK的上升沿输入，在存储寄存器时钟RCK的上升沿进入的存储寄存器中去。如果两个时钟连在一起，则移位寄存器总是比存储寄存器早一个脉冲。移位寄存器有一个串行移位输入端（SI）和一个用于级联的串行输出端（SQH）,8位移位寄存器可以异步复位（低电平复位），存储寄存器有一个8位三态并行的总线输出，当输出使能（OE）被使能（低电平有效）将存储寄存器中输出至74HC595N的引脚（总线）。

![](media/858b189f06ad68afe051b15043b2affd.png)

引脚说明：

|13引脚OE|是一个输出使能引脚，用于确保锁存器的数据是否输入到Q0-Q7引脚。在低电平时，不输出高电平。在本实验中，我们直接连接GND，保持低电平输出数据。|
|-|-|
|14引脚SI|这是74HC595接收数据的引脚，即串行数据输入端，一次只能输入一位，那么连续输入8次，就可以组成一个字节了。|
|10引脚SCLR|一个初始化存储寄存器管脚的管脚。在低电平时初始化内部存储寄存器。在这个实验中，我们连接VCC以保持高水平。|
|11引脚SCK|移位寄存器的时钟引脚，上升沿时，移位寄存器中的数据整体后移，并接收新的数据输入|
|12引脚RCK|存储寄存器的时钟输入引脚。上升沿时，数据从移位寄存器转存到存储寄存器中。这时数据就从Q0~Q7端口并行输出。|
|9引脚SQH|引脚是一个串行输出引脚，专门用于芯片级联，接下一个74HC595的SI端|
|15脚，1-7脚Q0--Q7|八位并行输出端，可以直接控制数码管的8个段|

采用VCC和GND为芯片供电，工作电压为5V

4.项目电路图和接线图：

![](media/c5ff7d1df69133b45b74f538f155d68d.png)

注意：需要注意74HC595N芯片插入的方向

![](media/b36bafc31eb701c04d0bfe1956c6952a.png)![](media/5a0de137092d094f6007098ac141586a.png)

![](media/3ab2cafb465c8b2690689239eac22261.png)

5.项目代码：

```c
int data = 4;// 将74hc5954引脚设置为数据输入引脚SI
int clock = 6;// 将74hc595的6引脚设置为时钟引脚SCK
int latch = 5;// 将74hc595的引脚5设置为输出锁存器RCK
int ledState = 0;
const int ON = HIGH;
const int OFF = LOW;

void setup()
{
    pinMode(data, OUTPUT);
    pinMode(clock, OUTPUT);
    pinMode(latch, OUTPUT);
}

void loop()
{
    for(int i = 0; i < 256; i++)
    {
        updateLEDs(i);
        delay(500);
    }
}

void updateLEDs(int value)
{
    digitalWrite(latch, LOW);//
    shiftOut(data, clock, MSBFIRST, ~value);// 串行数据输出，高电平优先
    digitalWrite(latch, HIGH);// 锁存器
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，可以看到7个LED灯亮灭变化情况，循环往复进行。

### 项目10: 一位数码管

1.  项目介绍：

七段数码管是一种显示十进制数字的电子显示设备，广泛应用于数字时钟、电子仪表、基本计算器和其他显示数字信息的电子设备。甚至我们在电影中看到的炸弹也有七个部分。也许七段数码管看起来不够现代，但它们是更复杂的点阵显示器的替代品，在有限的光线条件下和强烈的阳光下都很容易使用。在这个项目中，我们将使用Plus
控制板控制一位数码管显示数字。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/75e38d601750a4707369bc73d8028063.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|一位数码管*1|220Ω电阻*8|面包板*1|跳线若干|USB 线*1|

3. 元件知识：

![](media/e44a0f27beec739ee13e68c04865989f.png)

一位数码管显示原理：数码管显示是一种半导体发光器件。它的基本单元是一个发光二极管(LED)。数码管显示根据段数可分为7段数码管和8段数码管。8段数码管比7段多一个LED单元(用于小数点显示)。七段LED显示屏的每段是一个单独的LED。根据LED单元接线方式，数码管可分为共阳极数码管和共阴极书案管。

在共阴极7段数码管中，分段LED的所有阴极(或负极)都连接在一起，你应该把共阴极连接到GND，要点亮一个分段LED，你可以将其关联的引脚设置为HIGH。

在共阳极7段数码管中，所有段的LED阳极(正极)都连接在一起，你应该把共阳极连接到+5V。要点亮一个分段LED，你可以将其关联的引脚设置为LOW。

![](media/28fd057848fbe0e8c8e3362768e7aa44.png)

数码管的每个部分由一个LED组成。所以当你使用它的时候，你也需要使用一个限流电阻。否则，LED会被烧坏。在这个实验中，我们使用了一个普通的共阴极一位数码管。正如我们上面提到的，你应该将公共阴极连接到GND。要点亮一个分段LED，你可以将其关联的引脚设置为HIGH。

4.项目电路图和接线图：

![](media/00ef471b0ca7eff3c0e8419e7dae11de.png)

注意：插入面包板的七段数码管方向与接线图一致，右下角多一个点。

![](media/66da2f88234019c4a712494174ea4426.png)

![](media/46ee92ff3b54236d1d21dcb51f2c7020.png)

5.项目代码：

数字显示分7段，小数点显示分1段。当显示某些数字时，相应的段将被点亮。例如，当显示数字1时，b和c段将被打开。我们为每个数字编译子程序，并编译主程序以每1秒显示一个数字，循环显示数字9
~ 0。每个数字的显示时间取决于延迟时间，延迟时间越长，显示时间越长。

```c
// 设置每段的IO引脚
int a=7;// 设置a段数字引脚为7
int b=6;// 设置b段数字引脚为6
int c=5;// 设置c段数字引脚为5
int d=10;//设置d段数字引脚为10
int e=11;//设置e段数字引脚为11
int f=8;//数字f段数字引脚为8
int g=9;//设置g段数字引脚为9
int dp=4;//设置dp段数字引脚为4

void digital_0(void) // 显示数字0
{
    unsigned char j;
    digitalWrite(a,HIGH);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e,HIGH);
    digitalWrite(f,HIGH);
    digitalWrite(g,LOW);
    digitalWrite(dp,LOW);
}

void digital_1(void) //显示数字1
{
    unsigned char j;
    digitalWrite(c,HIGH);// 将5脚设为高电平，点亮c段led
    digitalWrite(b,HIGH);// 点亮b段led
    for(j=7;j<=11;j++)// 关闭其它段led
    	digitalWrite(j,LOW);
    digitalWrite(dp,LOW);// 关闭dp段led
}

void digital_2(void) // 显示数字2
{
    unsigned char j;
    digitalWrite(b,HIGH);
    digitalWrite(a,HIGH);
    for(j=9;j<=11;j++)
    	digitalWrite(j,HIGH);
    digitalWrite(dp,LOW);
    digitalWrite(c,LOW);
    digitalWrite(f,LOW);
}

void digital_3(void) // 显示数字3
{
    digitalWrite(g,HIGH);
    digitalWrite(a,HIGH);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(dp,LOW);
    digitalWrite(f,LOW);
    digitalWrite(e,LOW);
}

void digital_4(void) // 显示数字4
{digitalWrite(c,HIGH);
digitalWrite(b,HIGH);
digitalWrite(f,HIGH);
digitalWrite(g,HIGH);
digitalWrite(dp,LOW);
digitalWrite(a,LOW);
digitalWrite(e,LOW);
digitalWrite(d,LOW);
}

void digital_5(void) // 显示数字5
{
    unsigned char j;
    digitalWrite(a,HIGH);
    digitalWrite(b, LOW);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e, LOW);
    digitalWrite(f,HIGH);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
}

void digital_6(void) // 显示数字6
{
    unsigned char j;
    for(j=7;j<=11;j++)
    	digitalWrite(j,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(dp,LOW);
    digitalWrite(b,LOW);
}

void digital_7(void) // d显示数字7
{
    unsigned char j;
    for(j=5;j<=7;j++)
    	digitalWrite(j,HIGH);
    digitalWrite(dp,LOW);
    for(j=8;j<=11;j++)
    	digitalWrite(j,LOW);
}

void digital_8(void) // 显示数字8
{
    unsigned char j;
    for(j=5;j<=11;j++)
    	digitalWrite(j,HIGH);
    digitalWrite(dp,LOW);
}

void digital_9(void) // 显示数字9
{
    unsigned char j;
    digitalWrite(a,HIGH);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e, LOW);
    digitalWrite(f,HIGH);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
}

void setup()
{
    int i;// 设置变量i
    for(i=4;i<=11;i++)
    	pinMode(i,OUTPUT);// 设置引脚4-11为“输出”
}

void loop()
{
    while(1)
    {
        digital_9();// 显示数字9
        delay(1000); // 等待1秒
        digital_8();// 显示数字8
        delay(1000); // 等待1秒
        digital_7();// 显示数字7
        delay(1000); // 等待1秒
        digital_6();// 显示数字6
        delay(1000); // 等待1秒
        digital_5();// 显示数字5
        delay(1000); // 等待1秒
        digital_4();// 显示数字4
        delay(1000); // 等待1秒
        digital_3();// 显示数字3
        delay(1000); // 等待1秒
        digital_2();// 显示数字2
        delay(1000); // 等待1秒
        digital_1();// 显示数字1
        delay(1000);// 等待1秒
        digital_0();// 显示数字0
        delay(1000);// 等待1秒
    }
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，一位数码管将显示从9到0的数字。

### 项目11：四位数码管

1.  项目介绍：

4位7段数码管是一种非常实用的显示器件。电子时钟的显示，球场上的记分员，公园里的人数都是需要的。由于价格低廉，使用方便，越来越多的项目将使用4位7段数码管。在这个项目中，我们使用Plus
控制板控制4位7段数码管来显示0000-9999之间的数字。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/ee7a4ecd35ef268149e31fb9d62c8227.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|四位数码管*1|220Ω 电阻*8|跳线若干|面包板*1|USB 线*1|

3.  元件知识：

![](media/ce987bf9a2ab398945c98b34d3f8a003.png)

四位数码管：四位数码管有共阳极和共阴极两种四位数码管，显示原理是和一位数码管是类似的，都是8个GPIO口控制数码管的显示段，就是8个led灯，不过，这里是4位的，所以就还需要4个GPIO口来控制位选择端，就是选择哪个单个数码管亮，位的切换很快，肉眼区分不出来，就能看起来是多个数码管同时显示的了。

我们的四位数码管是共阴极的。

下图为4位数码管的引脚图，G1、G2、G3、G4就是控制位的引脚。

![](media/37113fa53213973132086c285d67686b.png)

下图为4位数码管内部布线原理图

![](media/c210e9899274cdf79ffd44db1e250fb5.png)![](media/ea75d1b7414bf6f8c187fb32fea9bc83.png)

4.项目的电路图和接线图：

对于四位数码管，限流电阻是必不可少的。这里我们使用220Ω的8个电阻。

![](media/71da40f847f2c27e83a2766e7f933605.png)

![](media/5b97deb00879da72894bfcdc94a9a3c6.png)

5.项目代码：

```c
int a = 6;
int b = 7;
int c = 8;
int d = 9;
int e = 10;
int f = 11;
int g = 12;
int dp = 13;
int g4 = 5;
int g3 = 4;
int g2 = 3;
int g1 = 2;

// 设置变量
long n = 1230;
int x = 100;
int del = 55;    // 时钟微调

void setup()
{
  pinMode(g1, OUTPUT);
  pinMode(g2, OUTPUT);
  pinMode(g3, OUTPUT);
  pinMode(g4, OUTPUT);
  pinMode(a, OUTPUT);
  pinMode(b, OUTPUT);
  pinMode(c, OUTPUT);
  pinMode(d, OUTPUT);
  pinMode(e, OUTPUT);
  pinMode(f, OUTPUT);
  pinMode(g, OUTPUT);
  pinMode(dp, OUTPUT);
}

void loop()
{
  int a=0;
  int b=0;
  int c=0;
  int d=0;
  unsigned long currentMillis = millis();
  while(d>=0)
  {
    while(millis()-currentMillis<10)
    {
      Display(1,a);
      Display(2,b);
      Display(3,c);
      Display(4,d);
    }
    currentMillis = millis(); 
    d++;  
  	if (d>9) 
  	{
      c++;
      d=0;
    }
    if (c>9) 
    {
  	  b++;
      c=0;
    }
    if (b>9) 
    {
      a++;
      b=0;
    }
    if (a>9) 
    {
      a=0;
      b=0;
      c=0;
      d=0;
    }
  }  
}

void WeiXuan(unsigned char n)//
{
  switch (n)
  {
    case 1:
      digitalWrite(g1, LOW);
      digitalWrite(g2, HIGH);
      digitalWrite(g3, HIGH);
      digitalWrite(g4, HIGH);
      break;
    case 2:
      digitalWrite(g1, HIGH);
      digitalWrite(g2, LOW);
      digitalWrite(g3, HIGH);
      digitalWrite(g4, HIGH);
      break;
    case 3:
      digitalWrite(g1, HIGH);
      digitalWrite(g2, HIGH);
      digitalWrite(g3, LOW);
      digitalWrite(g4, HIGH);
      break;
    case 4:
      digitalWrite(g1, HIGH);
      digitalWrite(g2, HIGH);
      digitalWrite(g3, HIGH);
      digitalWrite(g4, LOW);
      break;
    default :
      digitalWrite(g1, HIGH);
      digitalWrite(g2, HIGH);
      digitalWrite(g3, HIGH);
      digitalWrite(g4, HIGH);
      break;
  }
}

void Num_0()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, HIGH);
  digitalWrite(f, HIGH);
  digitalWrite(g, LOW);
  digitalWrite(dp, LOW);
}

void Num_1()
{
  digitalWrite(a, LOW);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, LOW);
  digitalWrite(e, LOW);
  digitalWrite(f, LOW);
  digitalWrite(g, LOW);
  digitalWrite(dp, LOW);
}

void Num_2()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, LOW);
  digitalWrite(d, HIGH);
  digitalWrite(e, HIGH);
  digitalWrite(f, LOW);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}

void Num_3()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, LOW);
  digitalWrite(f, LOW);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}

void Num_4()
{
  digitalWrite(a, LOW);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, LOW);
  digitalWrite(e, LOW);
  digitalWrite(f, HIGH);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}
void Num_5()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, LOW);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, LOW);
  digitalWrite(f, HIGH);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}

void Num_6()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, LOW);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, HIGH);
  digitalWrite(f, HIGH);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}
void Num_7()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, LOW);
  digitalWrite(e, LOW);
  digitalWrite(f, LOW);
  digitalWrite(g, LOW);
  digitalWrite(dp, LOW);
}

void Num_8()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, HIGH);
  digitalWrite(f, HIGH);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}

void Num_9()
{
  digitalWrite(a, HIGH);
  digitalWrite(b, HIGH);
  digitalWrite(c, HIGH);
  digitalWrite(d, HIGH);
  digitalWrite(e, LOW);
  digitalWrite(f, HIGH);
  digitalWrite(g, HIGH);
  digitalWrite(dp, LOW);
}

void Clear()    // 清屏
{
  digitalWrite(a, LOW);
  digitalWrite(b, LOW);
  digitalWrite(c, LOW);
  digitalWrite(d, LOW);
  digitalWrite(e, LOW);
  digitalWrite(f, LOW);
  digitalWrite(g, LOW);
  digitalWrite(dp, LOW);
}

void pickNumber(unsigned char n)// 选择数字
{
  switch (n)
  {
    case 0: Num_0();

      break;
    case 1: Num_1();
      break;
    case 2: Num_2();
      break;
    case 3: Num_3();
      break;
    case 4: Num_4();
      break;
    case 5: Num_5();
      break;
    case 6: Num_6();
      break;
    case 7: Num_7();
      break;
    case 8: Num_8();
      break;
    case 9: Num_9();
      break;
    default: Clear();
      break;
  }
}

void Display(unsigned char x, unsigned char Number)//    以x为坐标，显示数字
{
  WeiXuan(x);
  pickNumber(Number);
  delay(1);
  Clear() ; // 清屏
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，四位数码管显示0000-9999之间的数字。

### 项目12：点阵屏显示

1.  项目介绍：

点阵屏是一种电子数字显示设备，可以显示机器、钟表、公共交通离场指示器和许多其他设备上的信息。LED点阵显示能够满足不同应用需求，具有广阔的发展前景。LED点阵采用低压扫描，具有省电、使用寿命长、成本低、亮度高、视角宽、视野长、防水、规格多等优点。在这个项目中，我们将进行一个8\*8LED点阵显示实验，亲身体验它的魅力。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/d226a1f3c801ac78321f0692143c853e.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|8*8点阵屏*1|220Ω 电阻*8|跳线若干|面包板*1|USB 线*1|

3.  元件知识：

![](media/d226a1f3c801ac78321f0692143c853e.png)

8\*8点阵屏：8\*8的点阵由64个LED组成，每个LED被放置在一排和一列的交叉点上。点阵屏的外部视图如下所示：

![](media/b8a10d032797c4874fe344f8758749a8.png)![](media/559b8d5ce30d16e26dfe1fc53cb49fae.jpeg)

当某一行(ROW)的电平为1，某一列(COL)的电平为0时，对应的LED会点亮。如果你想在第一个点上点亮LED，你应该设置引脚⑨为高电平，引脚⑬为低电平。如果你想在第一行点亮led，你应该设置引脚⑨为高电平，将引脚⑬、③、④、⑩、⑥、⑪、⑮和⑯设置为低电平。如果你想点亮第一列的led，将引脚⑬设置为低电平，将引脚⑨、⑭、⑧、⑫、①、⑦、②和⑤设置为高电平。

点阵屏的内部视图如下所示：

![](media/d83ddbc5286ef87ff73c76c5e296c230.png)

4. 项目电路图和接线图：

   ![](media/c1dbab91079fbad54f8dd98a36f75aec.png)

   ![](media/559b8d5ce30d16e26dfe1fc53cb49fae.jpeg)

   在面包板上接线时要正放（点阵屏上的788BS字样朝主控板方向）

   ![](media/a90917acf94f4ea265f53edfbc21e216.png)

5. 项目代码：

```c
int R[] = {2,3,4,5,6,7,8,9};
int C[] = {10,11,12,13,A0,A1,A2,A3};

unsigned char data_0[8][8] =
{
{0,0,1,1,1,0,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,0,1,1,1,0,0,0}
};

unsigned char data_1[8][8] =
{
{0,0,0,0,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,1,1,1,0,0}
};

unsigned char data_2[8][8] =
{
{0,0,1,1,1,0,0,0},
{0,1,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,1,0,0,0,0},
{0,0,1,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_3[8][8] =
{
{0,0,1,1,1,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,1,1,1,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_4[8][8] =
{
{0,1,0,0,0,0,0,0},
{0,1,0,0,1,0,0,0},
{0,1,0,0,1,0,0,0},
{0,1,1,1,1,1,1,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_5[8][8] =
{
{0,1,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_6[8][8] =
{
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_7[8][8] =
{
{0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,1,0,0,0,0},
{0,0,1,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_8[8][8] =
{
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

unsigned char data_9[8][8] =
{
{0,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,1,0,0},
{0,0,0,0,0,1,0,0},
{0,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0}
};

void Display(unsigned char dat[8][8])
{
    for(int c = 0; c<8;c++)
    {
        digitalWrite(C[c],LOW);
        for(int r = 0;r<8;r++)
        {
        	digitalWrite(R[r],dat[r][c]);
        }
        delay(1);
        Clear();
    }
}

void Clear()
{
    for(int i = 0;i<8;i++)
    {
        digitalWrite(R[i],LOW);
        digitalWrite(C[i],HIGH);
    }
}

void setup()
{
    for(int i = 0;i<8;i++)
    {
        pinMode(R[i],OUTPUT);
        pinMode(C[i],OUTPUT);
    }
}

void loop()
{
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_0);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_1);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_2);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_3);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_4);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_5);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_6);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_7);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_8);
  }
  for (int i = 1; i <= 100; i = i + (1)) 
  {
    Display(data_9);
  }
}
```

6.项目结果：

烧录好测试代码，按照接线图连接好线；上电后，8\*8点阵屏依次显示数字0~9，循环进行。

### 项目13: 小台灯

1.项目介绍：

你知道Arduino可以在你按下按键的时候点亮LED吗?
在这个项目中，我们将使用Plus控制板，一个按键开关和一个LED来制作一个小台灯。

2.项目代码：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/5b8fea4657b47510d199f740fdcaaa9d.png)|![](media/ef77f5a64c382157fc2dea21ec373fef.png)|![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)|![](media/9cab81f7da18c7b0c245ec2a2f614f3a.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|按键*1|红色 LED*1|10KΩ电阻*1|按键帽*1|
|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/845d05a6108b1662b828610ba9dcb788.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)||
|面包板*1|220Ω电阻*1|USB 线*1|跳线若干||

3.元件知识：

![](media/5b8fea4657b47510d199f740fdcaaa9d.png)

按键：按键可以控制电路的通断，把按键接入电路中，不按下按键的时候电路是断开的，

一按下按键电路就通啦，但是松开之后就又断了。可是为什么按下才通电呢？这得从按键的内部构造说起。没按下之前，电流从按键的一端过不去另一端，按键的两端就像两座山，中间隔着一条河，我们在这座山过不去另一座山；按下的时候，按键内部的金属片把两边连接起来让电流通过，就像搭了一座桥，把两座山连接起来。

按键内部结构如图：![](media/d2a204e61c768f18924150db58aee093.png)，未按下按键之前，1、2就是导通的，3、4也是导通的，但是1、3或1、4或2、3或2、4是断开（不通）的；只有按下按键时，1、3或1、4或2、3或2、4才是导通的。

在设计电路时，按键开关是最常用的一种元件。

按键的原理图:     
![](media/5e42fde9876f9be810d85a7fb8b331f7.png)
![](media/8677548f9e756281629430d66ba3a460.png)   4脚按键引脚图，管脚结构：  
![](media/5dc61e1cf09f876fc0e8e3c395517730.png)  
独立按键的引脚内部连接方式如下图，大家也可以自己用万用表测试一下：  
![](media/e1eef743f3319a4c897183f7b2de9149.png)

什么是按键抖动？

我们想象的开关电路是“按下按键-立刻导通”“再次按下-立刻断开”，而实际上并非如此。

按键通常采用机械弹性开关，而机械弹性开关在机械触点断开闭合的瞬间（通常
10ms左右），会由于弹性作用产生一系列的抖动，造成按键开关在闭合时不会立刻稳定的接通电路，在断开时也不会瞬时彻底断开。

![](media/4dbcca62c2d75cab03260584924a16d8.jpeg)

那又如何消除按键抖动呢？

常用除抖动方法有两种：软件方法和硬件方法。这里重点讲讲方便简单的软件方法。

我们已经知道弹性惯性产生的抖动时间为10ms
左右，用延时命令推迟命令执行的时间就可以达到除抖动的效果。

所以我们在代码中加入了0.05秒的延时以实现按键防抖的功能。

![](media/1497573e05f993b5f32923fcd6590a01.jpeg)  
4. 项目电路图和接线图：

![](media/8cc6cbc757bcb93ef203b8884f86ad86.png)

![](media/1225e5d672d96d87fcde7b74f53d7f41.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻和五色环10KΩ电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

5.项目代码：

```c
int buttonPin = 5; //按钮连接到数字5
int ledPin = 12; //LED连接到数字12
int ledState = LOW; // ledState记录LED状态
int buttonState; // buttonState记录按键状态
int lastButtonState = LOW; // lastbuttonState记录按键前一个状态
long lastDebounceTime = 0;
long debounceDelay = 50; //去除抖动时间

void setup() 
{
    pinMode(buttonPin, INPUT);
    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, ledState);
}

void loop()
{
    //reading用来存储buttonPin的数据
    int reading = digitalRead(buttonPin);
    // 一旦检测到数据发生变化，记录当前时间
    if (reading != lastButtonState)
    {
    	lastDebounceTime= millis();
    }
    // 等待50ms，再进行一次判断，是否和当前button状态相同
    // 如果和当前状态不相同，改变button状态
    // 同时，如果button状态为高（也就是被按下），那么就改变led的状态
    if ((millis() - lastDebounceTime) >debounceDelay) 
    {
        if (reading != buttonState)
        {
            buttonState = reading;
            if (buttonState == HIGH) 
            {
            	ledState= !ledState;
            }
        }
    }
    digitalWrite(ledPin, ledState);
    // 改变button前一个状态值
    lastButtonState = reading;
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，按下按钮，灯点亮。再按下按钮，灯熄灭。是不是很像个小台灯？

### 项目14: 电子沙漏

1.  项目介绍：

    古代人没有电子时钟，就发明了沙漏来测时间，沙漏两边的容量比较大，在一边装了细沙，中间有个很小的通道，将沙漏直立，有细沙的一边在上方，由于重力的作用，细沙就会往下流通过通道到沙漏的另一边，当细沙都流到下边了，就倒过来，把一天反复的次数记录下来，第二天就可以通过沙漏反复流动的次数而知道这一天大概的时间了。这一课我们将利用Plus     控制板控制倾斜开关和LED灯电子元件来模拟沙漏，制作一个电子沙漏。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/36f15610f430e5d5138f4e4fb721c40f.png)|![](media/ef77f5a64c382157fc2dea21ec373fef.png)|![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|倾斜开关*1|红色 LED*4|10KΩ电阻*1|
|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/845d05a6108b1662b828610ba9dcb788.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|
|面包板*1|220Ω电阻*4|USB 线*1|跳线若干|

3.元件知识：

![](media/8c40739f8e05f753f145420b421a0f47.png)

倾斜开关也叫数字开关。里面有一个可以滚动的金属球。采用金属球滚动与底部导电板接触的原理来控制电路的通断。当倾斜开关是滚珠型倾斜感应单方向性触发开关，当倾斜传感器向触发端（两根金属脚端）倾斜时，倾斜开关处于闭路状态，模拟端口的电压约为5V(二进制数为1023)。这样，LED会亮起。当倾斜开关在水平位置或向另一端倾斜时，倾斜开关处于开路状态，模拟端口的电压约为0V(0二进制)。LED将会关闭。在程序中，我们根据模拟端口的电压值，是否大于2.5V(512二进制)来判断开关是开还是关。

这里用倾斜开关的内部结构来说明它是如何工作的，显示如下图：

![](media/40bc569b295c4656bd973da4ad8734e2.png)

4.项目电路图和接线图：

![](media/c4367389f15b1cb0332b15b8ec4eeeb6.png)

![](media/dd879781b2aafe7f1bf501e768a74241.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻和五色环10KΩ电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

5.项目代码：

```c
const byte SWITCH_PIN = 4; // 将倾斜开关连接到D4
byte switch_state = 0;

void setup()
{
    for(int i=8;i<12;i++)
    {
    	pinMode(i, OUTPUT);
    }
    pinMode(SWITCH_PIN, INPUT);
    for(int i=8;i<12;i++)
    {
    	digitalWrite(i,0);
    }
    Serial.begin(9600);
}

void loop()
{
    switch_state = digitalRead(SWITCH_PIN);
    Serial.println(switch_state);
    if (switch_state == 0)
    {
        for(int i=8;i<12;i++)
        {
            digitalWrite(i,1);
            delay(1000);
        }
    }
    if (switch_state == 1)
    {
        for(int i=11;i>7;i--)
        {
            digitalWrite(i,0);
            delay(1000);
        }
    }
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，用手握住面包板。倾斜到一定角度，led就会一个一个亮起来。当回到上一个角度时，led会一个一个关闭。就像沙漏一样，随着时间的推移，沙子漏了出来。

### 项目 15: 人体红外传感器控制蜂鸣器

1.  项目介绍：

人体红外传感器测量运动物体发出的热的红外(IR)光。该传感器可以检测人、动物和汽车的运动，从而触发安全警报和照明。它们被用来检测移动，是安全的理想选择，如防盗警报和安全照明系统。在这个项目中，我们将使用人体红外传感器、蜂鸣器来检测有人或动物靠近时发出声音。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/d5c7319c4d13964c330f2359cd69339d.jpeg)|![](media/2daad5f4245578f0babf881d38aad4ab.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|人体红外传感器*1|面包板*1|
|![](media/4b4f653a76a82a3b413855493cc58fba.png)|![](media/af83fb90e77c0d8731149843e1df81a3.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|
|有源蜂鸣器*1|3P 双头连接线*1|USB 线*1|跳线若干|

3.  元件知识：

人体红外传感器：其原理是某些晶体，例如钽酸锂、硫酸三甘肽等受热时，晶体两端会产生数量相等、符号相反的电荷，将这些电荷经放大器可转换为电压输出。而人体是会释放红外线的，虽然比较微弱，但是还是可以检测得到的。人体红外传感器检测附近有人运动时，传感器信号端输出高电平1，否则输出低电平0。特别注意，这个传感器可以检测在运动中的人、动物和汽车，静止中的人、动物和汽车是检测不到的，检测最远距离大约为7米左右。

注意：人体红外传感器应避开日光、汽车头灯、白炽灯直接照射，也不能对着热源(如暖气片、加热器)或空调，以避免环境温度较大的变化而造成误报。同时还易受射频辐射的干扰。

传感器技术参数：

最大输入电压：DC 3.3 ~ 5 v

最大工作电流：50MA

最大功率：0.3W

工作温度：-20 ~ 85℃

输出高电平3V，低电平0 V

延迟时间：大约2.3到3秒钟

检测角度：大约100度

检测最远距离：大约 7米左右

指示灯输出(当输出高电平时，它将点亮)

引脚限制电流：50MA

传感器原理图：

![](media/9e1ec604aa6f9d4a3c1fe41d4bccd699.png)

传感器扩展板：当我们在做DIY实验时，我们经常会利用UNO R3控制板和其他传感器/模块搭配使用。为了方便接线，我们的最新一版传感器/模块使用了间距为2.54mm防反接口。为了兼容最新一版传感器/模块接口，我们特别设计了这一款扩展板。使用时，我们只需要将扩展板堆叠在UNO R3控制板上，利用一个特定接线连接传感器/模块。接线简单方便，由于是防反接口，线序固定，将不会再出现控制板连接传感器/模块时，因线序接反，导致烧坏传感器/模块现象。

为方便接线，扩展板上接口都带有丝印。3pin接口丝印一般为G V S，其中扩展板上所有的G代表GND，V代表VCC（5V）接口，S代表接口上方的数字口/模拟口。4pin/5pin接口左面都有对应接口丝印。扩展板上两边还自带间距为2.54mm的排母接口，接线顺序和UNO R3板的排母接口的线序一致。同时扩展板上自带一个复位按键，1个复位按键指示灯（D2）,1个电源指示灯（D1）。

为了方便将扩展板固定在其他设备，扩展板只带两个直径为3mm的定位孔大小。

扩展板规格参数：

兼容：UNO R3控制板

接口：间距为2.54mm 3pin防反接口

定位孔大小：直径为3mm

尺寸：68\*55\*21mm

重量：26g

传感器扩展板接口说明：

![](media/6656decfceb819620b71a888108ba572.jpeg)

4.  项目电路图和接线图：

![](media/d4462a6212cff9715160e409ea395368.png)

![](media/0158f4a11e5b406e59720eb914e61a99.png)

5.项目代码：

```c
int buzzerpin = 8; // 蜂鸣器引脚
int pirPin = 3; // 人体红外传感器引脚
int pirStat = 0; // 人体红外传感器状态

void setup() 
{
    pinMode(buzzerpin, OUTPUT);
    pinMode(pirPin, INPUT);
    Serial.begin(9600);
}

void loop()
{
    pirStat = digitalRead(pirPin);
    if (pirStat == HIGH)
    { // 如果检测到人体或动物运动时
        digitalWrite(buzzerpin, HIGH); // 蜂鸣器鸣叫
        Serial.println("Hey I got you!!!");
    }
    else
    {
        digitalWrite(buzzerpin, LOW); //如果未检测到人体或动物运动时，关闭蜂鸣器
    }
}
```

6.  项目结果：

烧录好项目代码，按照接线图连接好线，上电后，如果PIR红外传感器检测到附近有人移动，蜂鸣器就会发出警报，点击打开Arduino IDE上的串行监视器，你会看到“Hey I got you!!”。

### 项目16： I2C 1602 LCD

1.项目介绍：

在生活中，我们可以利用显示器等模块来做各种实验。你也可以DIY各种各样的小物件。例如，用一个温度传感器和显示器做一个温度测试仪，或者用一个超声波模块和显示器做一个距离测试仪。下面，我们将使用1602 I2C模块作为显示器，将其连接到Plus控制板上。将使用Plus控制板控制1602显示屏显示字符串。

2.项目元件：

| Keyes Uno Plus 控制板*1                         | 传感器扩展板*1                                   | I2C 1602 LCD*1                                  | 4P 双头连接线*1                                  | USB 线*1                                        |
| ----------------------------------------------- | ------------------------------------------------ | ----------------------------------------------- | ------------------------------------------------ | ----------------------------------------------- |
| ![](media/544243270a027fc8cfa58e0f651a7bf4.png) | ![](media/d5c7319c4d13964c330f2359cd69339d.jpeg) | ![](media/4356b8b9bf0c3997972f07a15df49fb5.png) | ![](media/95a1cc6f8904507e98ee123c7f824bea.jpeg) | ![](media/755ba492c38e44d91e8b2c120dc64904.png) |

3.元件知识：

![](media/4356b8b9bf0c3997972f07a15df49fb5.png)

LCD1602显示屏：显示屏有LCD 1602液晶显示屏和I2C 1602 LCD。但是我们在这个项目中使用的是一个I2C LCD 1602。LCD 1602显示屏可以显示16列2行字符。它能够显示数字、字母、符号、ASCII码等。如下所示是一个单色LCD1602显示屏（在工作时需要占用控制板的7个IO口）及其电路引脚图：

![](media/090f5c6d8d196d63357b164168f8b702.png)

I2C LCD1602显示屏集成了I2C接口，连接的串行输入&并行输出给LCD1602显示屏模块。这使得我们只要使用4条线路就可以来操作LCD1602。

![](media/a63f879379a6f5db68288af80a05474e.jpeg)

本模块使用的IC芯片为PCF8574T (PCF8574AT)，其默认I2C地址为0x27(0x3F)。

在液晶显示器的背面有一个金属电位器。你可以用螺丝刀（我们不提供，你们自备）转动电位器来调整对比度。

![](media/55b85cd6367eae74e04f67d222c24a7e.png)

请注意：当你旋转电位器时，屏幕将变得更亮或更暗，适当的角度将使字体更清晰。

I2C 1602 LCD 原理图：

![](media/3055dd93488bc9dd88d1de4ed74743c1.png)

I2C 1602 LCD技术参数：

显示像素：16 \* 2 字符

芯片工作电压：4.5 ~ 5.5V

工作电流：2.0mA (5.0V)

模块最佳工作电压：5.0V

I2C 地址：0x27

背光 (蓝色背景和白色背光)

4.项目接线图：

![](media/f1f3550080bbf207c8b2f30a7db40bf3.png)

5.  项目代码：

    注意：代码中需要安装库文件，如果已经添加了LiquidCrystal_I2C和Wire等库文件，就忽略下面库文件的添加过程。

    将文件夹中的库文件解压，即把解压后的LiquidCrystal_I2C文件夹和Wire文件夹放入编译器安装目录下的\Arduino\libraries里。

    放置成功后，需要重启编译器，不然编译不过。

    例如我的：C:\Program Files\Arduino\libraries

```c
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // 设置LCD地址为0x27

void setup()
{
    lcd.init(); // 初始化lcd
    lcd.init();
    lcd.backlight();
}

void loop()
{
    lcd.setCursor(3,0);
    lcd.print("Hello, world!");
    lcd.setCursor(2,1);
    lcd.print("keyestudio!");
}
```

6.项目结果：

烧录好项目代码，按照接线图连接好线，上电后，I2C 1602 LCD的第一行将显示Hello, world!，第二行将显示keyestudio!。通过更改我们提供的代码括号中的文本并再次上传代码，你可以通过I2C 1602 LCD看到显示的东西。

```
lcd.setCursor(3,0);

lcd.print("Hello, world!");

lcd.setCursor(2,1);

lcd.print("keyestudio!");
```

### 项目17：小风扇

1.  项目介绍：

在炎热的夏季，需要电扇来给我们降温，那么在这个项目中，我们将使用Plus控制板控制直流电机来制作一个小电扇。

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/5fe5f8cd6e75e7f8d4ec71f54a4ac2f5.png)|![](media/5eba8bae9e1d18b959ca425a9cc83fd2.jpeg)|![](media/4e0b78edf6e4aeefa4c5191c606b2031.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/df3db6765ee8c86beafa8410e87dd50d.png)|![](media/655e6c465cb423279e0908513a983711.png)|
|-|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|L293D芯片*1|直流电机*1|面包板*1|USB 线*1|跳线若干|风扇片*1|

3.元件知识:

![](media/5fe5f8cd6e75e7f8d4ec71f54a4ac2f5.png)

L293D芯片：L293D是一种直流电动驱动IC，在一些机器人项目中可用来驱动直流电机或步进电机。它共有16个引脚，可以同时驱动两路直流电机。输入电压范围：4.5 V ~ 36 V，每通道输出电流：MAX 600mA，可以驱动感性负载，特别是其输入端可以与单片机直接相连，从而很方便地受单片机控制。当驱动小型直流电机时，可以直接控制两路电机，并且可以实现电机正转与反转，实现此功能只需改变输入端的高低电平。市面上有许多采用L293D芯片的电机驱动板，当然我们也可以自己通过简单连接来使用它。

L293D引脚图：

![](media/2e5e0bd5b4577ac159d0568404dc21b5.png)

|引脚号|引脚名称|描述|
|-|-|-|
|1|Enable1|该引脚使能输入引脚Input 1(2)和Input 2(7)|
|2|In1|直接控制输出1引脚，由数字电路控制。|
|3|Out1|连接到电机1的一端|
|4|0V|接地引脚连接到电路的接地(0V)|
|5|0V|接地引脚连接到电路的接地(0V)|
|6|Out2|连接到电机1的另一端|
|7|In2|直接控制输出2引脚。由数字电路控制|
|8|+V motor|连接到运行电机的电压引脚(4.5V至36V)|
|9|Enable2|该引脚使能输入引脚输入3(10)和输入4(15)|
|10|In3|直接控制输出3引脚。由数字电路控制|
|11|Out3|连接到电机2的一端|
|12|0V|接地引脚连接到电路的接地(0V)|
|13|0V|接地引脚连接到电路的接地(0V)|
|14|Out4|连接到电机2的另一端|
|15|In4|直接控制输出4引脚，由数字电路控制|
|16|+V|连接到+ 5V以启用IC功能|

4.项目电路图和接线图：

![](media/ee2445c81c799b22ede245fcf8aa1684.png)

![](media/73c78c0d0fde448e6a382c200b8addd5.png)

5.  项目代码：

```c
int IN1=8;
int IN2=4;
int ENA=9;

void setup()
{
    pinMode(IN1,OUTPUT);
    pinMode(IN2,OUTPUT);
    pinMode(ENA,OUTPUT);
}

void loop()
{
    //向前转（正转）
    digitalWrite(IN1,LOW);
    digitalWrite(IN2,HIGH);
    analogWrite(ENA,200);
    delay(3000);
    //延迟3秒
    analogWrite(ENA,0);
    delay(1000);
    //向后转（反转）
    digitalWrite(IN1,HIGH);
    digitalWrite(IN2,LOW);
    analogWrite(ENA,100);
    delay(3000);
    //延时3秒
    analogWrite(ENA,0);
    delay(1000);
}
```

6.项目结果：

在控制板上上传代码成功，按照接线图接好线，并且把小风扇安装到电机上，上电后，可以看到小风扇向前旋转3秒，停止1秒，反转3秒，停止1秒，重复进行。ENA连接到Plus控制板的PWM引脚。可以通过控制PWM来控制小风扇的速度。在实验中，正向旋转速度明显快于反向旋转速度。

### 项目18: 舵机转动

1.  项目介绍：

舵机是一种可以非常精确地旋转的电机。目前已广泛应用于玩具车、遥控直升机、飞机、机器人等领域。在这个项目中，我们将使用Plus
控制板控制舵机转动。

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/0fe8dd5aa4e7a01d33c88a622c7f554f.jpeg)|![](media/cd0bc424e9916881a1a903793821a042.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|舵机*1|USB 线*1|

3.  元件知识：

舵机：

![](media/99830768916233a9c5900ac399006c17.png)

舵机是一种位置伺服的驱动器，主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。其工作原理是由接收机或者单片机发出信号给舵机，其内部有一个基准电路，产生周期为20ms，宽度为1.5ms的基准信号，将获得的直流偏置电压与电位器的电压比较，获得电压差输出。经由电路板上的IC判断转动方向，再驱动无核心马达开始转动，透过减速齿轮将动力传至摆臂，同时由位置检测器送回信号，判断是否已经到达定位。适用于那些需要角度不断变化并可以保持的控制系统。当电机转速一定时，通过级联减速齿轮带动电位器旋转，使得电压差为0，电机停止转动。一般舵机旋转的角度范围是0度到180度。

控制舵机的脉冲周期为20ms，脉冲宽度为0.5ms ~ 2.5ms，对应位置为-90°~+90°。下面是以一个180°角的舵机为例：

![](media/441a7943b07a25635095a850c356875b.jpeg)

伺服电机有多种规格，但它们都有三根连接线，分别是棕色、红色、橙色(不同品牌可能有不同的颜色)。棕色为GND，红色为电源正极，橙色为信号线。

![](media/e97b6b1ac383c3be75235da049ac10d5.png)

4.  项目接线图：

![](media/7e700c7f948c7d291236dfd2b4bea42a.png)

5.  项目代码：

    注意：代码中需要安装库文件，如果已经添加了Servo库文件，就忽略下面库文件的添加过程。

    将文件夹中的库文件解压，即把解压后的Servo文件夹放入编译器安装目录下的\Arduino\libraries里。

    放置成功后，需要重启编译器，不然编译不过。

    例如我的：C:\Program Files\Arduino\libraries

```c
#include <Servo.h>
Servo myservo;// 定义舵机名

void setup()
{
	myservo.attach(9);// 选择舵机引脚(9)
}

void loop()
{
    myservo.write(0);// 设置电机的旋转角度
    delay(500);
    myservo.write(45);// 设置电机的旋转角度
    delay(500);
    myservo.write(90);// 设置电机的旋转角度
    delay(500);
    myservo.write(135);// 设置电机的旋转角度
    delay(500);
    myservo.write(180);// 设置电机的旋转角度
    delay(500);
}
```

6. 项目结果：

将项目代码上传到Plus控制板。按照接线图接好线，上电后，舵机塑料臂将以0°、45°、90°、135°、180°的角度转动。循环进行！！

### 项目19：步进电机 

1.  项目介绍：

步进电机定位准确，是工业机器人、3D打印机、大型车床等机械设备中最重要的部件。在这个项目中，我们将使用
Plus 控制板控制一个步进电机转动。

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/52227712f82a477e2c6abfad08529e93.png)|![](media/8ebb14a35091dc8d02d95cb6748dd1e9.png)|![](media/df84a18afd8b37097c469ae3ac208bc4.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|ULN2003步进电机驱动板*1|步进电机*1|公对母杜邦线若干|USB 线*1|

3.  项目知识：

    ![](media/8ebb14a35091dc8d02d95cb6748dd1e9.png)

步进电机：是由一系列电磁线圈控制的电机。它可以根据需要旋转精确的度数(或步数)，允许你将它移动到一个精确的位置并保持该位置。它是通过在很短的时间内为电机内部的线圈供电来做到这一点的，但你必须一直为电机供电，以保持它在你想要的位置。有两种基本类型的步进电机，单极步进和双极步进。在本项目中，我们使用的是单极步进电机28-BYJ48。

![](media/78276dc113309152d542fc3f06a24757.png)

28BYJ-48步进电机工作原理：

步进电机主要由定子和转子组成，定子是固定不动的，如下图绕着A、B、C、D线圈组的部分，线圈组导通电就会产生磁场；转子就是转动的部分，如下图定子中间的部分，两极是永磁铁。

![](media/32748e0804b1fff434181cb228b23242.png)

单步4节拍的转动原理：开始A组线圈导通，转子两极正对着A组线圈；接着A组线圈断开，B组线圈导通，转子就会顺时针转到B组线圈，转子转了一步；B断开，C导通，转子转到C组；C断开，D导通，转子转到D组；D组断开，A组导通，转子转到A组线圈。这样转子就转了半圈180度，接着再重复一次，B-C-D-A，转子转回到A组线圈，这样转子就转了一圈，总共转动了8步。如下图所示，这就是步进电机单节拍转动的原理A -B - C - D - A ....。

如果想让步进电机逆时针转动，那只要把节拍顺序反过来就行，D - C - B - A -D .....。

![](media/b8ae50bbdee2dd5bc683e8c450baee6a.png)半步8节拍转动原理：8节拍，采用的是单双拍的形式，A - AB - B - BC - C -CD - D - DA - A ......，这样运转一拍，转子只会转动半步，例如，A组线圈导通，转子转到正对着A组线圈；接着A和B组一起导通，这样产生的磁场最强的地方在AB组线圈中间，转子两极就会转到AB组线圈中间，也就是顺时针转了半步。

步进电机参数：

我们所提供的步进电机需要转动32步，转子才能转一圈，还经过了1:64的减速齿轮组带动输出轴，这样输出轴转动一圈需要：32 \* 64 = 2048 步。

电压5V，4相步进电机 ，4节拍模式的步进角为11.25，8节拍模式步进角为5.625， 减速比为1:64

ULN2003步进电机驱动板：是步进电机驱动器。

下面的原理图显示了如何使用ULN2003步进电机驱动板接口将一个单极步进电机接到Plus控制板的引脚上，并显示了如何使用四个TIP120的接口。

![](media/b0996ef9dd0b1aff687bf48d0f6bbaf3.jpeg)

4.  项目原理图和接线图：

    ![](media/5158f2708809c74efe3a0bff738cea30.png)

![](media/15eef4f9196ca3f1430b9c25a66c4976.png)

5.项目代码：

```c
// 步进电机引脚
const int IN1_pin = 11;
const int IN2_pin = 10;
const int IN3_pin = 9;
const int IN4_pin = 8;

int val;

void setup() 
{
    Serial.begin(9600);
    // 步进电机的Arduino引脚设置
    pinMode(IN1_pin,OUTPUT);
    pinMode(IN2_pin,OUTPUT);
    pinMode(IN3_pin,OUTPUT);
    pinMode(IN4_pin,OUTPUT);
}

void loop()  
{
    int a = 1024;
    int b = 1024;
    val=Serial.read();
    if(val=='A')
    {
        while(a--)
        {
            digitalWrite(IN1_pin, HIGH);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN4_pin, LOW);
            delay(10);
            digitalWrite(IN1_pin, LOW);
            digitalWrite(IN2_pin, HIGH);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN4_pin, LOW);
            delay(10);
            digitalWrite(IN1_pin, LOW);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN3_pin, HIGH);
            digitalWrite(IN4_pin, LOW);
            delay(10);
            digitalWrite(IN1_pin, LOW);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN4_pin, HIGH);
            delay(10);
        }
    }
    if(val=='C')
    {
        while(b--)
        {
            digitalWrite(IN4_pin, HIGH);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN1_pin, LOW);
            delay(10);
            digitalWrite(IN4_pin, LOW);
            digitalWrite(IN3_pin, HIGH);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN1_pin, LOW);
            delay(10);
            digitalWrite(IN4_pin, LOW);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN2_pin, HIGH);
            digitalWrite(IN1_pin, LOW);
            delay(10);
            digitalWrite(IN4_pin, LOW);
            digitalWrite(IN3_pin, LOW);
            digitalWrite(IN2_pin, LOW);
            digitalWrite(IN1_pin, HIGH);
            delay(10);
        }
    }
    digitalWrite(IN4_pin, LOW);
    digitalWrite(IN3_pin, LOW);
    digitalWrite(IN2_pin, LOW);
    digitalWrite(IN1_pin, LOW);
}
```

6.项目结果：

将项目代码上传到PLUS开发板，按接线图接好线，上电后，打开串口监视器，设置波特率为9600，我们在串口监视器中输入“A”并点击“发送”，步进电机是反转，在串口监视器中输入“C”并点击“发送”，步进电机是正转。

### 项目20：继电器 

项目介绍：

在日常生活中，我们一般使用220V交流来驱动电气设备，有时我们会用开关来控制电器。如果将开关直接连接到220V交流电路上，一旦发生漏电，人就有危险。从安全的角度考虑，我们特别设计了这款具有NO(常开)端和NC(常闭)端的继电器模块。在这节课我们将学习一个比较特殊，好用的开关，就是继电器模块。

项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/0fe8dd5aa4e7a01d33c88a622c7f554f.jpeg)|![](media/c141037e768eebd697ea07520708ee47.png)|![](media/1e698295a5280aa518fb063cd87a80ae.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|继电器模块*1|3P 双头连接线*1|USB 线*1|

元件知识：

继电器：是用小电流去控制大电流运作的一种“自动开关”。

控制输入电压：5V

额定负载：5A 250VAC (NO/NC) 5A 24VDC (NO/NC)

额定负载的意思是，可以使用树莓派的5V控制直流电压24V或者交流电压250V的设备。

继电器原理图：

![](media/be1c90d2b52fc2489590e3f702a087bf.png)

1.  项目接线图：

![](media/93900d5c580e1c159435eede66b355a0.png)

2.  项目代码：

```c
int Relay = 3; //定义数字口3

void setup()
{
	pinMode(Relay, OUTPUT); //将Relay设置为输出
}

void loop()
{
    digitalWrite(Relay, HIGH); //打开继电器
    delay(2000); //延时2S
    digitalWrite(Relay, LOW); //关闭继电器
    delay(2000); //延时2S
}
```

3.  项目结果：

在控制板上上传代码成功，按照接线图接好线，上电后，继电器开启（ON端连通）2秒，停止（NC端连通）2秒，循环交替。开启时继电器上的LED亮起。

### 项目21：调光灯

1.  项目介绍：

电位器是一个带有滑动或旋转触点的三端电阻器，它形成一个可调的分压器。它的工作原理是在均匀电阻上改变滑动触点的位置。在电位器中，整个输入电压被施加到电阻的整个长度上，输出电压是固定触点和滑动触点之间的电压值。在这个项目中，我们将学习如何使用Arduino读取电位器的值，并制作一个可调光灯。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/8945ce5821e6e5843e1203e299b415f1.png)|![](media/ef77f5a64c382157fc2dea21ec373fef.png)|![](media/845d05a6108b1662b828610ba9dcb788.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|可调电位器*1|红色 LED*1|200Ω电阻*1|
|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)||
|面包板*1|USB 线*1|跳线若干||

3.  元件知识：

![](media/c397aba3de644bb70ffa7a9139a5499e.png)

可调电位器：可调电位器是电阻和模拟电子元件的一种，具有0和1两种状态(高电平和低电平)。模拟量不同，其数据状态呈现为1
~ 1024等线性状态。

4.  读取电位器模拟值：

我们将可调电位器连接到Arduino的模拟引脚上读取其值。接线请参照以下接线图：

![](media/c8cc7ff54c6dd885eeeb20f32f2e04b5.png)

```c
int potpin=A1;//初始化可调电位器的模拟引脚A1
int val=0;// 定义val,初始值赋为0

void setup()
{
	Serial.begin(9600);// 波特率设置为9600
}

void loop()
{
    val=analogRead(potpin);// 读取模拟引脚A1的模拟值，并将其赋值给val 
    Serial.println(val);// 显示val的值
}
```

将代码上传到Plus控制板上，按照接线图连接好线，上电后，当你旋转电位器旋钮时，你可以看到显示的值发生变化。由于大多数传感器输出的是模拟值，因此模拟值的读取是一个非常常见的功能。经过计算，可以得到所需的对应值。下图显示了它读取的模拟值。

![](media/69187b6a6204ec9be9e987bcb35e306f.png)

5.调光灯的电路图和接线图：

在前面一步，我们读取了可调电位器的模拟值，现在我们需要将电位器的模拟值转换成LED的亮度，做成一个亮度可调的灯。见接线图。

![](media/17b9ea36b6c54576692526a11dce7b25.png)

![](media/703df5358a560ff1ac54e2570de1900d.png)

6.项目代码：

```c
int potpin=A1;// 初始化可调电位器的模拟引脚A1
int ledpin=11;// 初始化数字引脚11
int val=0;// 定义val,初始值赋为0

void setup()
{
    pinMode(ledpin,OUTPUT);// 设置数字引脚为“输出”
    Serial.begin(9600);// 波特率设置为9600
}

void loop()
{
    val=analogRead(potpin);// 读取模拟引脚A1的模拟值，并将其赋值给val 
    analogWrite(ledpin,val/4);
    Serial.println(val);// 显示val的值
}
```

7.项目结果：

在控制板上上传代码成功，按照接线图接好线，上电后，打开串口监视器，设置波特率为9600，监视器将显示电位器的模拟值。当我们转动电位器时，LED的亮度会发生变化。

### 项目22: 火焰报警

1.  项目介绍：

火灾是一种可怕的灾害，火灾报警系统在房屋，商业建筑和工厂中非常有用。在本项目中，我们将使用火焰传感器和蜂鸣器来制作火灾报警装置。这是一个有意义的创客活动。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/882d7aebfdcc637edf3dd161675dc24e.png)|![](media/4b4f653a76a82a3b413855493cc58fba.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/7a9793b2ed75473d4338c84f1419b7df.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)|
|-|-|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|火焰传感器*1|有源蜂鸣器*1|面包板*1|跳线若干|USB 线*1|10KΩ电阻*1|

3.  元件知识：

![](media/398d6477d6a8949031d52f141a250142.png)

火焰会发出一定程度的IR光，这种光人眼是看不到的，但我们的火焰传感器可以检测到它，并提醒微控制器，如Arduino已经检测到火灾。它有一个专门设计的红外接收管来探测火焰，然后将火焰亮度转换为波动水平信号。接收三极管的短引脚是负极，另一个长引脚是正极。我们应该连接短引脚（负极)到5V，连接长
引脚(正极)到模拟引脚，一个电阻和GND。如下图所示：

![](media/3f0435fdd13d8f1845a04014709d0f41.jpeg)

4.  读取火焰传感器模拟值：

我们首先用一个简单的代码读取火焰传感器的值，把它打印在串行监视器上。接线请参照以下接线图：

![](media/3be5b777b212a7b954e5b7ac6ab84847.png)

```c
int flamepin=A1;// 初始化模拟管脚A1
int val=0;// 定义val，初始值赋为0

void setup()
{
	Serial.begin(9600);// 波特率设置为9600
}

void loop()
{
    val=analogRead(flamepin);// 读取模拟引脚A1的模拟值，并将其值赋给val
    Serial.println(val);// 显示val值
}
```

将代码上传到Plus控制板，按照接线图连接好线，上电后。打开串行监视器，用打火机火焰接近火焰传感器查看其模拟值。

![](media/54ccb4c3dd27ae9ac3bb5d3e499d5cb0.png)

5.  火焰报警的电路图和接线图：

接下来，我们将使用火焰传感器和蜂鸣器、RGB LED制作一个有趣的项目——火焰报警。当检测到火焰时，RGB亮红灯，蜂鸣器报警。

![](media/ee51f8ddfb54811148fba03b399deb1f.png)

![](media/81b580d04a0b02ac931f8fcf2df82077.png)

6.  项目代码：

```c
const int red = 11;
const int green = 10;
const int blue= 9;
const int buzzer = 12;
const int flamepin = A1;
const int thereshold = 30;

void setup() 
{
    // 将设置代码放在这里，运行一次:
    Serial.begin(9600);
    pinMode(red, OUTPUT);
    pinMode(green, OUTPUT);
    pinMode(blue, OUTPUT);
    pinMode(buzzer, OUTPUT);
    pinMode(flamepin, INPUT);
}

void setColor(int redValue, int greenValue, int blueValue)
{
    analogWrite(red, redValue);
    analogWrite(blue, blueValue);
    analogWrite(green, greenValue);
}

void loop() 
{
    // 把主代码放在这里，重复运行:
    int flamesenseval = analogRead(flamepin);
    Serial.println(flamesenseval);
    if (flamesenseval >= thereshold) 
    {
        setColor(255, 0, 0); //红色
        tone(buzzer, 1000);
        delay(10);
    }
    else
    {
        setColor(0, 255, 0); // 绿色
        noTone(buzzer);
    }
}
```

7.项目结果：

将代码上传到PLUS控制板，按照接线图连接好线，上电后。打开串口监视器，设置波特率为9600，监视器将显示火焰传感器的值。我们使用打火机火焰靠近火焰传感器，RGB LED亮红灯，蜂鸣器会报警；否则RGB LED亮绿灯，蜂鸣器不响。

### 项目23: 光控灯

1.项目介绍：

传感器或元件在我们的日常生活中是无处不在的。例如，一些公共路灯在晚上会自动亮起，而在白天会自动熄灭。为什么呢?
事实上，这些都是利用了一种光敏元件，可以感应外部环境光强度的元件。晚上，当室外亮度降低时，路灯会自动打开；到了白天，路灯会自动关闭。这其中的原理是很简单的，这节课我们就实现这个路灯的功能。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/942378c667f9389955ad7d43d74d7615.png)|![](media/ef77f5a64c382157fc2dea21ec373fef.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|光敏电阻*1|红色 LED*1|220Ω电阻*1|
|![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/7a9793b2ed75473d4338c84f1419b7df.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|10KΩ电阻*1|面包板*1|跳线若干|USB 线*1|

3.  元件知识：

![](media/4f79b3be4e1f694675264534d0d10c74.png)

光敏电阻：光敏传感器是利用半导体的光电导效应制成的一种电阻值随入射光的强弱而改变的电阻器，又称为光电导探测器。周围的光变强，电阻变小，模拟信号就变大；反之，光变弱，电阻增大，模拟信号就变小。

光敏传感器常用的制作材料为硫化镉，另外还有硒、硫化铝、硫化铅和硫化铋等材料。这些制作材料具有在特定波长的光照射下，其阻值迅速减小的特性。这是由于光照产生的载流子都参与导电，在外加电场的作用下作漂移运动，电子奔向电源的正极，空穴奔向电源的负极，从而使光敏传感器的阻值迅速下降。

光敏电阻普遍应用于光的测量、光的控制和光伏转换(将光的变化转化为电能的变化)。光敏电阻也被广泛应用于各种光控电路，如光控调节、光开关等。

![](media/2efcbe4de13ae5184b90ab11edadf5e9.jpeg)

我们将从一个相对简单的关于光敏变阻器应用的实验开始。

4.  读取光敏电阻模拟值：

我们首先用一个简单的代码读取光电池的值，将其打印在串行监视器中。接线请参照以下接线图：

![](media/9f86e125b836ab8c1cfbdd880bcc46b1.png)

```c
int photocellpin=A0;// 初始化连接光敏电阻的模拟管脚A0
int val=0;// 初始化变量val的值为0

void setup()
{
	Serial.begin(9600);// 波特率设置为9600
}

void loop()
{
    val=analogRead(photocellpin);// 读取传感器的值并将其值赋给val
    Serial.println(val);// 显示val的值
    delay(200);// 等待0.2秒
}
```

将代码上传到Plus控制板上，按照接线图连接好线，上电后，打开串口显示器，就可以读取光敏电阻的模拟值。逐渐减弱光敏电阻所处环境中的光照强度，你会发现串口显示器上显示的模拟值在逐渐变小了；反之，模拟值在逐渐增大。

![](media/da0c5d0b5de5e060a6b370e06537f880.png)

5.  光控灯的电路图和接线图：

我们在前面做了一个小的调光灯，现在我们做一个光控灯。它们的原理是相同的。即通过Arduino获取传感器的模拟值，然后调节LED的亮度。![](media/abbea59da20eabb5e1ebfd32d4f02ade.png)

![](media/dc96f248437a7c8fb1788da1aae25b96.png)

6.  项目代码：

```c
int photocellpin=A0;// 初始化连接光敏电阻的模拟管脚A0
int ledpin=11;// 初始化数字管脚11
int val=0;// 初始化变量val的值为0

void setup()
{
    pinMode(ledpin,OUTPUT);// 设置数字引脚11为“输出”
    Serial.begin(9600);// 波特率设置为9600	
}

void loop()
{
    val=analogRead(photocellpin);//读取传感器的模拟值并将其值赋给val
    Serial.println(val);//显示val的值
    analogWrite(ledpin,val/4);//设置亮度(最大值255)
    delay(10);// 等待0.01秒
}
```

7.  项目结果：

将代码上传到PLUS控制板。按照接线图连接好线，上电后，打开串口显示器，设置波特率为9600。监视器将显示光敏电阻的模拟值。当逐渐减弱光敏电阻所处环境中的光照强度时，串口显示器上显示的模拟值在逐渐变小，LED会变暗。当逐渐减强光敏电阻所处环境中的光照强度时，显示的模拟值会变大，LED会变亮。

### 项目24: 超声波测距仪

1.  项目介绍：       HC-SR04超声波传感器是一种非常实惠的距离传感器，主要用于各种机器人项目中的物体躲避。它也被用于水位传感，甚至作为一个停车传感器。我们把超声波传感器当作蝙蝠的眼睛。在黑暗中，蝙蝠仍然可以通过超声波识别前方的物体和方向。

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/0fe8dd5aa4e7a01d33c88a622c7f554f.jpeg)|![](media/11163a0fe0cd3efaf4ccd57fb237f103.png)|![](media/7eb361d680dfa351f07f8527aeb37abd.png)|![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|超声波传感器*1|红色LED*4|220Ω电阻*4|
|![](media/95a1cc6f8904507e98ee123c7f824bea.jpeg)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)||
|4P双头连接线*1|USB 线*1|面包板*1|跳线若干||

3.  元件知识：

    HC-SR04超声波传感器：像蝙蝠一样使用声纳来确定与物体的距离，它提供了精准的非接触范围检测，高精度和稳定的读数。它的操作不受阳光或黑色材料的影响，就像精密的照相机(在声学上像布料这样比较软的材料很难被探测到)。它带有超声波发射器和接收器。

![](media/653a2e40ce31d039c801edce1050114d.png)

在超声波传感器的前面是两个金属圆筒，这些是转换器。转换器将机械能转换成电信号。在超声波传感器中，有发射转换器和接收转换器。发射转换器将电信号转换为超声波脉冲，接收转换器将反射的超声波脉冲转换回电信号。如果你看超声波传感器的背面，你会看到的发射转换器后面有一个IC。这是控制发射转换器的IC。在接收转换器后面也有一个IC，这是一个四运算放大器，它将接收转换器产生的信号放大成足以传输到Arduino的信号。

时序图：

图示HC-SR04的时序图。为了开始测量，SR04的Trig必须接受至少10us的高(5V)脉冲，这将启动传感器将发射出8个周期的40kHz的超声波脉冲，并等待反射的超声波脉冲。当传感器从接收器检测到超声波时，它将设置回波引脚为高(5V)和延迟一个周期(宽度)，与距离成比例。为了获得距离，测量Echo引脚的宽度。

![](media/ba43be6007d9fe3aab0bb609868af640.png)

时间=回波脉冲宽度，单位为us(微秒)

距离厘米=时间/ 58

距离(英寸)=时间/ 148

4.  读取超声波传感器距离值

    我们将从一个简单的超声波测距开始，在串行监视器里输出测量的距离。

    ![](media/8835e046cfb2a4b1d6da5e78c473ccce.png)

    HC-SR04超声波传感器有四个引脚：Vcc、Trig、Echo和GND。Vcc引脚提供产生超声波脉冲的电源，接Vcc/+5V。GND引脚接地/GND。Trig引脚是Arduino发送信号来启动超声波脉冲的地方。Echo引脚是超声波传感器向Plus控制板发送关于超声波脉冲行程持续时间的信息的地方。按下图接线：

![](media/8549b59407a993c9197cd3af90416d01.png)

```c
const int trig = 12;
const int echo = 13;

int duration = 0;
int distance = 0;

void setup()
{
    pinMode(trig , OUTPUT);
    pinMode(echo , INPUT);
    Serial.begin(9600);
}

void loop()
{
    digitalWrite(trig , HIGH);
    delayMicroseconds(1000);
    digitalWrite(trig , LOW);
    duration = pulseIn(echo , HIGH);
    distance = (duration/2) / 28.5 ;
    Serial.print(distance);
    Serial.println("cm");
}
```

上传好代码到Plus控制板，按接线图接好线，上电后，再打开串行监视器，设置波特率为9600，当把一个物体放在超声波传感器前面时(远近)，它会检测到物体的距离，该值将显示在监视器上。

![](media/21571b712e81bf91e33194e728681729.png)

5.  超声波测距仪的电路图和接线图：

    接下来，我们将使用超声波传感器和4个led制作一个简单的超声波测距仪。按下图接好线。

    ![](media/b1e3d4a390893dd9e055da78478d4f43.png)

![](media/f43813f90fcfeb89f963f4367764188c.png)

6.  项目代码：

```c
const int trig = 12;
const int echo = 13;
const int LED1 = 11;
const int LED2 = 10;
const int LED3 = 9;
const int LED4 = 8;
int duration = 0;
int distance = 0;

void setup()
{
    pinMode(trig , OUTPUT);
    pinMode(echo , INPUT);
    pinMode(LED1 , OUTPUT);
    pinMode(LED2 , OUTPUT);
    pinMode(LED3 , OUTPUT);
    pinMode(LED4 , OUTPUT);
    Serial.begin(9600);
}

void loop()
{
    digitalWrite(trig , HIGH);
    delayMicroseconds(1000);
    digitalWrite(trig , LOW);
    duration = pulseIn(echo , HIGH);
    distance = (duration/2) / 28.5;
    Serial.println(distance);
    if ( distance <= 7 )
    {
    	digitalWrite(LED1, HIGH);
    }
    else
    {
    	digitalWrite(LED1, LOW);
    }
    if ( distance <= 14 )
    {
    	digitalWrite(LED2, HIGH);
    }
    else
    {
    	digitalWrite(LED2, LOW);
    }
    if ( distance <= 21 )
    {
    	digitalWrite(LED3, HIGH);
    }
    else
    {
    	digitalWrite(LED3, LOW);
    }
    if ( distance <= 28 )
    {
    	digitalWrite(LED4, HIGH);
    }
    else
    {
    	digitalWrite(LED4, LOW);
    }
}
```

7.项目结果：

将代码上传到PLUS控制板。按接线图接好线，上电后，超声波模块可以检测前方障碍物的距离。另外，当我们用手在超声波传感器前移动时，相应的LED会亮起来.

### 项目25：摇杆控制步进电机

1.  项目介绍：

摇杆模块是一个有两个模拟输入和一个数字输入的组件。广泛应用于游戏操作、机器人控制、无人机控制等领域。在这个项目中，我们使用Plus
控制板和一个摇杆模块控制步进电机转动。你可以在实践中对摇杆模块的原理和操作有更深入的了解。 

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/0fe8dd5aa4e7a01d33c88a622c7f554f.jpeg)|![](media/43acc7d0046b997a4822d8fdab834c55.png)|![](media/5212612a405474d94078cd01a3742af0.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|摇杆模块*1|步进电机*1|
|![](media/8f01847e44d1d2b496d39659a4056119.png)|![](media/c2a46b26a3ea6fa72b4402a8dfa5ba4e.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/3b53f13a6a0dce0af068adbd371cba35.png)|
|ULN2003步进电机驱动板*1|5P双头连接线*1|USB 线*1|公对母杜邦线若干|

3.  元件知识：

    ![](media/43acc7d0046b997a4822d8fdab834c55.png)

摇杆模块：主要是采用PS2
手柄摇杆元件，实际上摇杆模块有3个信号端引脚，模拟3维空间，摇杆模块的引脚分别是GND、VCC、信号端（B、X、Y），其中信号端X、Y模拟空间的X轴和Y轴，控制时，模块的X、Y信号端是连接单片机模拟口，通过控制2个模拟输入值来控制物体在空间X、Y轴的坐标。信号端B模拟空间Z轴，它一般是接数字口，做按键使用。

VCC接单片机电源输出端V/VCC（3.3/5V），GND接单片机G/GND，原始状态下读出电压大约为1.65V/2.5V左右，对于X轴方向，当随箭头方向逐渐按下，读出电压值随着增加，且可以达到最大电压，随箭头相反方向逐渐按下，读出电压值逐渐减少，减少到最小电压；对于Y轴方向，当沿着模块上的箭头方向逐渐按下，读出电压值逐渐减少，减少到最小电压，随箭头相反方向逐渐按下，读出电压值随着增加，且可以达到最大电压；对于Z轴方向，信号端B接数字口，原始状态下输出0，按下输出1。这样，我们可以读取两个模拟值和一个数字口的高低电平情况，判断模块上摇杆的工作状态。

4.  读取摇杆模块的值：

我们必须使用模拟Arduino引脚从X/Y引脚读取数据，并使用数字引脚读取按钮。请按照下面的接线图进行接线：

![](media/82d15703e20a5957b666210e1fe7cd78.png)

```c
int VRx = A0;
int VRy = A1;
int SW = 7;
int xPosition = 0;
int yPosition = 0;
int SW_state = 0;
int mapX = 0;
int mapY = 0;

void setup() 
{
    Serial.begin(9600);
    pinMode(VRx, INPUT);
    pinMode(VRy, INPUT);
    pinMode(SW, INPUT_PULLUP);
}

void loop() 
{
    xPosition = analogRead(VRx);
    yPosition = analogRead(VRy);
    SW_state = digitalRead(SW);
    mapX = map(xPosition, 0, 1023, -512, 512);
    mapY = map(yPosition, 0, 1023, -512, 512);
    Serial.print("X: ");
    Serial.print(mapX);
    Serial.print(" | Y: ");
    Serial.print(mapY);
    Serial.print(" | Button: ");
    Serial.println(SW_state);
    delay(100);
}
```

上传代码到Plus控制板，按接线图接好线，上电后，打开串口显示器，设置波特率为9600。当你摇动摇杆或按下按钮时，你可以在串行监视器上看到它们的值。

![](media/cb37cffa9b7507533f876455c8cc7613.png)

5.  摇杆模块控制步进电机的电路图和接线图：

我们刚读了摇杆模块的值，这里我们需要用摇杆模块和步进电机做一些事情，按照下图连接：

![](media/215f6a9bcc67921b367e8ae020705107.png)

![](media/8e639fae73763b601cc3cd18beccef53.png)

6.  项目代码：

```c
const int X_pin = 0; // 模拟引脚A0连接到X输出
const int Y_pin = 1; // 模拟引脚A1连接到Y输出

int SW_pin = 3;
int X_Rotate;
int Y_Rotate;

// 步进电机引脚
const int IN1_pin = 11;
const int IN2_pin = 10;
const int IN3_pin = 9;
const int IN4_pin = 8;

void setup() 
{
    // 摇杆的Arduino引脚设置
    pinMode(SW_pin, INPUT);
    digitalWrite(SW_pin, HIGH);
    // Arduino pin setup for stepper motor
    pinMode(IN1_pin,OUTPUT);
    pinMode(IN2_pin,OUTPUT);
    pinMode(IN3_pin,OUTPUT);
    pinMode(IN4_pin,OUTPUT);
}

void loop() 
{
    X_Rotate = analogRead(X_pin);
    Y_Rotate = analogRead(Y_pin);
    if (Y_Rotate < 500) 
    {
        digitalWrite(IN1_pin, HIGH);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN4_pin, LOW);
        delay((Y_Rotate/2)+2);
        digitalWrite(IN1_pin, LOW);
        digitalWrite(IN2_pin, HIGH);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN4_pin, LOW);
        delay((Y_Rotate/2)+2);
        digitalWrite(IN1_pin, LOW);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN3_pin, HIGH);
        digitalWrite(IN4_pin, LOW);
        delay((Y_Rotate/2)+2);
        digitalWrite(IN1_pin, LOW);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN4_pin, HIGH);
        delay((Y_Rotate/2)+2);
    }
    else if (Y_Rotate > 550)
    {
        digitalWrite(IN4_pin, HIGH);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN1_pin, LOW);
        delay((1028-Y_Rotate)/2);
        digitalWrite(IN4_pin, LOW);
        digitalWrite(IN3_pin, HIGH);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN1_pin, LOW);
        delay((1028-Y_Rotate)/2);
        digitalWrite(IN4_pin, LOW);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN2_pin, HIGH);
        digitalWrite(IN1_pin, LOW);
        delay((1028-Y_Rotate)/2);
        digitalWrite(IN4_pin, LOW);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN1_pin, HIGH);
        delay((1028-Y_Rotate)/2);
    }
    else if (Y_Rotate > 500 && Y_Rotate < 550) 
    {
        digitalWrite(IN4_pin, LOW);
        digitalWrite(IN3_pin, LOW);
        digitalWrite(IN2_pin, LOW);
        digitalWrite(IN1_pin, LOW);
    }
}
```

7.  项目结果：

将项目代码上传到Plus开发板，按照接线图接好线，上电后，沿着Y轴正方向推一下摇杆，步进电机就会正转；反之，沿着Y轴反方向推一下摇杆，步进电机就会反转。

### 项目26: 红外遥控

1.  项目介绍：

红外遥控是一种低成本、易于使用的无线通信技术。IR光与可见光非常相似，除了它的波长稍长一点。这意味着红外线是人眼无法检测到的，这对于无线通信来说是完美的。例如，当你按下电视遥控器上的一个按钮时，一个红外LED会以每秒38000次的频率反复开关，将信息(如音量或频道控制)传送到电视上的红外感光器。

我们将首先解释常见的红外通信协议是如何工作的。然后我们将从一个遥控器和一个红外接收组件开始这个项目。我们准备了一个家庭卡通板。当我们按遥控器的按钮时，房子上的灯就会亮，再按一下按钮，它就会灭。

2.  项目元件：

| ![](media/544243270a027fc8cfa58e0f651a7bf4.png) | ![](media/3ec5906fad2172708d449390140f55e6.png) | ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png) | ![](media/e380dd26e4825be9a768973802a55fe6.png) | ![](media/e9a8d050105397bb183512fb4ffdd2f6.png) |
| ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- |
| Keyes Uno Plus 控制板*1                         | 红色LED*3                                       | 220Ω电阻*3                                      | 面包板*1                                        | 跳线若干                                        |
| ![](media/8a6fb37dedef748e6c2609bff5c64906.png) | ![](media/88e6b057fb4b0c576c9b2111d15b26e5.png) | ![](media/a22dac8c5edbe90e867cbb04769d1816.png) | ![](media/755ba492c38e44d91e8b2c120dc64904.png) |                                                 |
| 红外遥控器*1                                    | 红外接收器 *1                                   | 10KΩ电阻*1                                      | USB 线*1                                        |                                                 |
3.  元件知识（红外线遥控器是如何工作的 ）：

    红外是什么?

    红外线辐射是一种光，类似于我们在周围看到的光。红外线和可见光之间唯一的区别是频率和波长。红外线辐射在可见光范围之外，所以人类看不见它:

![](media/f7b16bff5d6cd9d3e9e80b6754156287.jpeg)

因为红外线是一种光，红外通信要求从接收器到发射机的视线是直的。它不能通过墙壁或其他材料，如WiFi或蓝牙进行传输。

红外和接收器是如何工作的？

典型的红外通信系统需要一个红外发射器和一个红外接收器。发射器看起来就像一个标准的LED，除了它产生的光是红外光谱而不是可见光谱。如果你看一下电视遥控器，你会看到红外线发射器。

LED:![](media/28c0a8a6423fa66794502e6db19f997d.jpeg)

红外接收器是一个光电二极管和前置放大器，将红外光转换为电信号。红外接收器二极管通常是这样的：![](media/bd2e58b0d6cf1f050eed1245345a2bad.jpeg)

红外信号调制：

红外线是由太阳、灯泡和其他任何产生热量的物质发出的。这意味着我们周围有很多红外光噪声。为了防止这种噪声干扰红外信号，采用了信号调制技术。在红外信号调制中，红外遥控器上的编码器将二进制信号转换为已调制的电信号。这个电信号被发送到发射LED。所述发射LED将所述调制电信号转换为调制的红外光信号。红外接收器然后解调红外光信号，并将其转换回二进制，然后将信息传递给微控制器。

![](media/b664b5b6884f7d3d8b4ee7eebdfd699b.png)

被调制的红外信号是一系列的红外光脉冲在一个被称为载频的高频开关和关闭。大多数发射机使用的载波频率是38千赫，因为它在自然界中是罕见的，因此可以区别于环境噪声。通过这种方式，红外接收器将知道38千赫的信号是从发射机发出的，而不是从周围环境接收到的。接收二极管检测所有频率的红外光，但它有一个带通滤波器，只能让38千赫的红外光通过。然后，它用前置放大器放大调制信号，并将其转换为二进制信号，然后将其发送给微控制器。

IR代码：

每次按下遥控器上的按钮，就会生成一个唯一的十六进制代码。这是经过调制并通过IR发送到接收器的信息。为了破译哪个键被按下，接收微控制器需要知道哪个代码对应于遥控器上的每个键。

不同的远程为按键发送不同的代码，因此您需要确定为特定的远程上的每个键生成的代码。如果您能找到数据表，应列出IR键码。如果没有，还有一个简单的Arduino草图，它可以读取大多数流行的遥控器，并在你按下一个键时将十六进制代码打印到串行显示器上。我将在一分钟内向您展示如何设置，但首先我们需要将接收器连接到Arduino Plus控制板。

4.  解码红外信号：

我们按照下面接线图将红外接收模块连接到Plus开发板上。

![](media/9a036eab5f79082aa1db4c1b7d66a8b7.png)

安装IR remote库：

我们将在下面的所有代码示例中使用IR remote库。

注意：代码中需要安装库文件，如果已经添加了Arduino-IRremote-master库文件，就忽略下面库文件的添加过程。

将文件夹中的库文件解压，即把解压后的Arduino-IRremote-master文件夹放入编译器安装目录下的\Arduino\libraries里。

放置成功后，需要重启编译器，不然编译不过。

例如我的：C:\Program Files\Arduino\libraries

```c
#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
    Serial.begin(9600);
    irrecv.enableIRIn(); // 开始接收信号
}

void loop() 
{
    if (irrecv.decode(&results)) 
    {
        Serial.println(results.value, HEX);
        irrecv.resume(); // 接收下一个值
    }
    delay(100);
}
```

将此代码上传到Plus板，按照接线图连接好线，上电后，并以9600的波特率打开串行监视器。

![](media/182f4b55797db30e68f4141642832f95.png)

你会在串行显示器上看到一个代码。多次按下相同的按钮以确保你拥有该按钮的正确代码。如果看到FFFFFFFF，请忽略它。

![](media/6f2eaabf3671afb30676299a775fd88d.png)

写下红外遥控器与每个按钮相关联的代码，因为你稍后将需要这些信息。

![](media/ebcf0cb2055f7784505f76ceeaef9f47.jpeg)

5. 红外遥控的电路图和接线图：

现在，我将向您展示如何控制Arduino的输出引脚使用红外遥控。在这个项目中，我们将点亮LED。您可以很容易地修改代码来做一些事情，如控制伺服电机，或激活从遥控器的任何按钮按下继电器。将led与电阻连接到引脚8,9,10。

![](media/b5456fed5201b6ca4c5090221f4bc856.png)

![](media/389e6848ad13674ca61ece59c95aa5f3.png)

6.项目代码：

```c
#include <IRremote.h>
int IR_Recv = 11; //红外接收器引脚11
int bluePin = 10;
int greenPin = 9;
int yellowPin = 8;
IRrecv irrecv(IR_Recv);
decode_results results;

void setup()
{
    Serial.begin(9600); //开始串行通信
    irrecv.enableIRIn(); // 开始接收
    pinMode(bluePin, OUTPUT); // 设置数字引脚为输出
    pinMode(greenPin, OUTPUT); // 设置数字引脚为输出
    pinMode(yellowPin, OUTPUT); // 设置数字引脚为输出
}

void loop()
{
    //解码红外信号输入
    if (irrecv.decode(&results))
    {
        long int decCode = results.value;
        Serial.println(results.value,HEX);
        //切换case 以使用所选的远程控制按钮
        switch (results.value)
        {
            case 0x00FF6897: //当你按1键时
                digitalWrite(bluePin, HIGH);
                break;
            case 0x00FF30CF: //当你按4键时
                digitalWrite(bluePin, LOW);
                break;
            case 0x00FF9867: //当你按2键时
                digitalWrite(greenPin, HIGH);
                break;
            case 0x00FF18E7: //当你按5键时
                digitalWrite(greenPin, LOW);
                break;
            case 0x00FFB04F: //当你按3键时
                digitalWrite(yellowPin, HIGH);
                break;
            case 0x00FF7A85: //当你按6键时
                digitalWrite(yellowPin, LOW);
                break;
        }
        irrecv.resume(); // 从你按下的按钮接收下一个值
    }
    delay(10);
}
```

注意:
将我们在对应的文件夹中提供的IRremote库文件夹添加到安装目录Arduino编译器库中，否则编译失败。

7.  项目结果：

将代码上传到开发板，按照接线图连接好线，上电后，按按钮1和4打开和关闭第一个LED。按按钮2和5控制第二个LED。按3、6键控制第三个LED的状态。

### 项目27：温度仪表

1.  项目介绍：

热敏电阻是一种电阻，其阻值取决于温度和温度的变化。因此，我们可以利用这一特性来制作温度计。

2.  项目元件：

|![](media/65355a20ac3dfb3e0b48011c3e7947ea.png)|![](media/0fe8dd5aa4e7a01d33c88a622c7f554f.jpeg)|![](media/95a1cc6f8904507e98ee123c7f824bea.jpeg)|![](media/b45bb81bb3763377c63accce606ac5f2.png)|![](media/5ba197df049decb301e344dcba8a9e1f.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|4P双头连接线*1|热敏电阻*1|4.7KΩ电阻*1|
|![](media/d84f766a5b9b394448ffbd53e0f545d0.jpeg)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e380dd26e4825be9a768973802a55fe6.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)||
|I2C1602LCD*1|USB 线*1|面包板*1|跳线若干||

3.  元件知识：

    热敏电阻：热敏电阻是一种温度敏感电阻。当它感应到温度的变化时，热敏电阻的电阻就会改变。我们可以利用这一特性，用热敏电阻来检测温度强度。借此广泛应用于园艺、家庭警报系统等装置中。

    ①这里使用的是NTC-MF52AT     10K热敏电阻，其中B为3950，它与R<sub>S</sub>=R<sub>平衡</sub>=4.7KΩ电阻串联，热敏电阻的电阻值会随着温度的变化而改变。

    ![](media/cbc30b533808ec7540328c0d932ce600.jpeg)

    ②NTC热敏电阻的计算：       NTC 热敏电阻温度计算公式：Rt = R\*EXP\[B\*(1/T1-1/T2)\]

其中，T1和T2指的是K度，即开尔文温度。

Rt是热敏电阻在T1温度下的阻值。

R是热敏电阻在T2常温下的标称阻值，10K的热敏电阻25℃的值为10K（即R=10K）。T2=(273.15+25)

EXP\[n\]是e的n次方

B值是热敏电阻的重要参数,B=3950。

我们可以利用ADC转换器测得的值来得到热敏电阻的电阻值，然后再用公式来得到温度值。因此，摄氏温度t=((T1\*B)/(B+T1\*ln(Rt/R1)))-273.15，这里可以将ln换算成log，即t=((T1\*B)/(B+T1\*log(Rt/R1)))-273.15。同时±0.5的误差矫正。

4.  读取热敏电阻的值：

    首先我们学习了如何使用串行监视器来打印热敏电阻的值。请按下面的接线图接好线：

![](media/e6217a640ee88c14405501f8ae647d8d.png)

```c
#include<math.h>

const float voltagePower=5.0;
const float Rs=4.7;//采样电阻为4.7千欧
const int B=3950;
const double T1=273.15+25;//常温
const double R1=10;//常温对应的阻值，注意单位是千欧

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{ 
  //获得A1处的电压值
  double digitalValue=analogRead(1);
  double voltageValue=(digitalValue/1023)*5;
  Serial.print("Current voltage value=");
  Serial.println(voltageValue);
  
  //通过分压比获得热敏电阻的阻值
  double Rt=((voltagePower-voltageValue)*Rs)/voltageValue;
  Serial.print("Current registor value=");
  Serial.println(Rt);
 
  //换算得到温度值
  Serial.print("Current temperature value=");
  Serial.println(((T1*B)/(B+T1*log(Rt/R1)))-273.15);//
  Serial.println();
    
  //每3秒输出，更改此处修改频率
  delay(3000); 
}
```

将代码上传到Plus控制板，按接线图接好线，上电后，打开串行监视器，就可以读取热敏电阻引脚A1处的电压值，通过分压比获得热敏电阻的阻值和温度值。如下所示：

![](media/03f893c5688cde54482fbfb08fb6ea81.png)

5.  温度仪表电路图和接线图：

![](media/1da9667325d1a52d66540bb25775fd93.png)

![](media/b2d0c081b246d1bbeab09ede4e4a3c93.png)

6.  项目代码：

    注意：代码中需要安装I2C 1602     LCD库文件，如果已经添加了I2C 1602     LCD库文件，就忽略下面库文件的添加过程。

    项目16中包含有I2C 1602     LCD的库文件，将文件夹中的库文件解压，即把解压后的LiquidCrystal_I2C文件夹放入编译器安装目录下的\Arduino\libraries里。

    放置成功后，需要重启编译器，不然编译不过。

例如我的：C:\Program Files\Arduino\libraries

```c
#include <math.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // 设置LCD地址为0x27, 16个字符，2行显示
const float voltagePower=5.0;
const float Rs_val=4.7;//采样电阻为4.7千欧
const int B=3950;
const double T1=273.15+25;//常温
const double R1=10;//常温对应的阻值，注意单位是千欧

void setup()
{
    Serial.begin(9600);
    lcd.init();                      // 初始化 lcd
    // 打印消息到LCD.
    lcd.backlight();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("C v v=");
}

void loop()
{
    //获得A1处的电压值
    double digitalValue=analogRead(1);
    double voltageValue=(digitalValue/1023)*5;  
    //通过分压比获得热敏电阻的阻值
    double Rt=((voltagePower-voltageValue)*Rs_val)/voltageValue;
    //换算得到温度值
    const float t=((T1*B)/(B+T1*log(Rt/R1)))-273.15;
    if(t>-100.0) //如果温度大于-100℃时，串口监视器显示电压值,通过分压比获得热敏电阻的阻值,温度值和LCD显示电压值和温度值
    {
        Serial.print("Current voltage value=");
        Serial.println(voltageValue);
        Serial.print("Current registor value=");
        Serial.println(Rt);
        Serial.print("Current temperature value=");
        Serial.println(t);
        Serial.println("  ");
        lcd.setCursor(7, 0);
        lcd.print(voltageValue);
        lcd.setCursor(13, 0);
        lcd.print("V");
        lcd.setCursor(0, 1);
        lcd.print("C t v=");
        lcd.setCursor(7, 1);
        lcd.print(t);
        lcd.setCursor(13, 1);
        lcd.print("C");
        lcd.print("  ");
    }
    else
    {
    	Serial.println("Error! check sensor!");
    }
    delay(500);
}
```

7.  项目结果：

将项目代码上传到Plus开发板，按照接线图接好线，上电后，I2C 1602 LCD显示A1引脚的电压值和当前环境中的温度值。同时打开串口监视器窗口，可以看到相应的A1引脚的电压值，通过分压比获得热敏电阻的阻值电阻值和当前环境中的温度值。

### 项目28：4x4矩阵键盘控制LED

项目介绍：

常用的数字按钮传感器，一个按钮就使用一个IO口，而有时我们需要的按钮比较多时，就会占用过多的IO口，为了节省IO口的使用，把多个按钮做成了矩阵类型，通过横纵端的控制，实现少IO口控制多个按钮。这节课我们将来学习有关薄膜4\*4矩阵键盘。

1.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/fcd187eb009098d691927511606c991b.jpeg)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|![](media/e9a8d050105397bb183512fb4ffdd2f6.png)|
|-|-|-|-|
|Keyes Uno Plus 控制板*1|薄膜4*4矩阵键盘*1|USB 线*1|跳线若干|

2.  元件知识：

    4\*4矩阵键盘：键盘是一种集成了许多键的设备。如下图所示，一个4x4键盘集成16个键。

    ![](media/fcd187eb009098d691927511606c991b.jpeg)

    与LED矩阵集成一样，在4x4键盘中，每排键都是用一根引脚连接，每一列键都是一样的。这样的连接可以减少处理器端口的占用。内部电路如下所示。

    ![](media/5ebdacba906622079e0ef41dc1ea3fdf.png)

使用方法类似于矩阵LED，即使用行扫描或列扫描方法检测每列或每行上的键的状态。以列扫描法为例，向第4列(Pin4)发送低电平，检测第1、2、3、4行电平状态，判断A、B、C、D键是否按下。然后依次将低电平发送到列3、2、1，检测是否有其它键被按下。然后，你可以获得所有键的状态。

3.  读取4\*4矩阵键盘的键值：

    我们首先使用一个简单的代码读取4\*4矩阵键盘的键值，将其打印在串行监视器中，其接线图如下所示：

    ![](media/bc26fbd72127daf9329e5070087afea2.png)安装Keypad库：

    我们将在下面的所有代码示例中使用Keypad库。

    注意：代码中需要安装库文件，如果已经添加了Keypad库文件，就忽略下面库文件的添加过程。

    将文件夹中的库文件解压，即把解压后的Keypad文件夹放入编译器安装目录下的\Arduino\libraries里。

    放置成功后，需要重启编译器，不然编译不过。

    例如我的：C:\Program Files\Arduino\libraries

```c
#include <Keypad.h>
const byte ROWS = 4; // 定义行4
const byte COLS = 4; // 定义列4 

char keys[ROWS][COLS] = 
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
// 将4*4键盘的行端口连接到控制板上相应的数字IO
byte rowPins[ROWS] = {2,3,4,5};
//将4*4键盘的列端口连接到控制板上相应的数字IO
byte colPins[COLS] = {6,7,8,9};
// 从键盘类库调用相应函数
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup()
{
    Serial.begin(9600);
    }
    void loop()
    {
        char key = keypad.getKey();
        if (key != NO_KEY){
        Serial.println(key);
    }
}
```

将代码上传到Plus控制板，按接线图接好线，上电后，打开串行监视器，按下4\*4矩阵键盘的键，在打开串行监视器里可以读取对应的键值，例如：我们可以从下图看到，当我们按下#时，它将显示#。

![](media/feb2348c1a69915c40d74279309d1936.png)

4.  项目代码：(4\*4键盘控制LED实验)

    使用上面的连接图。在这里，我们使用4\*4矩阵键盘的键来控制控制板上引脚13的LED。

```c
#include <Keypad.h>
const byte ROWS = 4; // 定义行4
const byte COLS = 4; // 定义列4

char keys[ROWS][COLS] = 
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};

// 将4*4键盘的行端口连接到控制板上相应的数字IO
byte rowPins[ROWS] = {2,3,4,5};
// 将4*4键盘的列端口连接到控制板上相应的数字IO
byte colPins[COLS] = {6,7,8,9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
byte ledPin = 13;
boolean blink = false;

void setup()
{
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT); // 设置数字引脚为输出
    digitalWrite(ledPin, HIGH); // 设置LED亮
    keypad.addEventListener(keypadEvent); //为这个小键盘添加EventListener
}

void loop()
{
    char key = keypad.getKey();
    if (key != NO_KEY) 
    {
    	Serial.println(key);
    }
    if (blink)
    {
        digitalWrite(ledPin,!digitalRead(ledPin));
        delay(100);
    }
}

//处理一些特殊事件
void keypadEvent(KeypadEvent key)
{
    switch (keypad.getState())
    {
        case PRESSED:
        switch (key)
        {
            case '#': 
                digitalWrite(ledPin,!digitalRead(ledPin)); 
                break;
            case '*':
                digitalWrite(ledPin,!digitalRead(ledPin));
                break;
        }
        break;
        case RELEASED:
            switch (key)
            {
                case '*':
                    digitalWrite(ledPin,!digitalRead(ledPin));
                    blink = false;
                    break;
            }
            break;
        case HOLD:
            switch (key)
            {
                case '*': blink = true; break;
             }
            break;
    }
}
```

注意:
将我们在对应的文件夹中提供的Keypad库文件夹添加到安装目录Arduino编译器库中，否则编译失败。

5.  项目结果：

将代码上传到Plus控制板，按接线图接好线，上电后，当你按\*键时，Plus控制板上的pin 13的LED会一直亮直到你松开它。当你按下#键，然后释放它，LED会一直亮，直到你再次按下这个键。

### 项目29：WIFI 测试

1.  项目介绍：

    ESP8266串口WIFI ESP-01模块，它是一款超低功耗的UART-WiFi     透传模块，拥有业内极富竞争力的封装尺寸和超低能耗技术，专为移动设备和物联网应用设计，可将用户的物理设备连接到Wi-Fi     无线网络上，进行互联网或局域网通信，实现联网功能。

2.  项目元件：

|![](media/408f9f823aab6078768f08462eda209b.png)|![](media/a25740165807a76ccad82e817d9f1f31.png)|
|-|-|
|ESP8266串口WIFI ESP-01*1|USB转ESP-01S WIFI模块串口测试扩展板*1|

3.  元件知识：

![](media/a25740165807a76ccad82e817d9f1f31.png)

USB转ESP-01S WIFI模块串口测试扩展板：适用于ESP-01S WiFi模块，扩展板的拨动开关打到flash boot端，直插于电脑USB口，用安信可串口调试助手测试AT指令。

扩展板的拨动开关打到Uart Download
端，直插于电脑USB口，ESP-01模块处于下载模式，通过安信可固件下载软件可下载固件到ESP-01模块中。

![](media/408f9f823aab6078768f08462eda209b.png)

ESP8266串口WIFI ESP-01：ESP8266串口WIFI ESP-01是一款超低功耗的UART-WiFi 透传模块，ESP8266串口WIFI ESP-01可广泛应用于智能电网、智能交通、智能家具、手持设备、工业控制等领域。

（1）产品特性：

\*\* 支持无线802.11 b/g/n 标准

\*\* 支持STA/AP/STA+AP 三种工作模式

\*\* 内置TCP/IP协议栈，支持多路TCP Client连接

\*\* 支持丰富的Socket AT指令

\*\* 支持UART/GPIO数据通信接口

\*\* 支持Smart Link 智能联网功能

\*\* 支持远程固件升级（OTA）

\*\* 内置32位MCU，可兼作应用处理器

\*\* 超低能耗，高集成度的 Wi-Fi 芯片，适合电池供电应用

\*\* 超宽工作温度范围：-40°C 至 +125°C

\*\* 3.3V 单电源供电


3.  功耗：

    下列功耗数据是基于3.3V的电源、25°的环境温度下测得。

    1.所有测量均在天线接口处完成。

    2.所有发射数据是基于 90% 的占空比，在持续发射的模式下测得的。

（5）射频指标：

以下数据是在室内温度下，电压为3.3V时测得。

|描述|最小值|通常|最大值|单位|
|-|-|-|-|-|
|输入频率|2412||2484|MHz|
|输入电阻||50||Ω|
|输入反射|||-10|dB|
|72.2Mbps下，PA的输出功率|14|15|16|dBm|
|802.11b模式下，PA的输出功率|17.5|18.5|19.5|dBm|

灵敏度
|CCK 1Mbps||-98||dBm|
|-|-|-|-|-|
|CCK 11Mbps||-91||dBm|
|6Mbps(1/2BPSK)||-93||dBm|
|54Mbps(3/4 64-QAM)||-75||dBm|
|HT20，MCS7（65Mbps，72.2Mbps）||-71||dBm|

邻频抑制
|OFDM，6Mbps||37||dB|
|-|-|-|-|-|
|OFDM，54Mbps||21||dB|
|HT20，MCS0||37||dB|
|HT20，MCS7||20||dB|

注：1. 72.2Mbps是在802.11n模式下，MCS=7，GI=200uS时测得；

2\. 802.11b模式下最高可达+19.5dBm的输出功率。

（6）功能描述：

A.主要功能

ESP8266可以实现的主要功能包括：串口透传，PWM 调控，GPIO控制。

※串口透传：数据传输，传输的可靠性好，最大的传输速率为：460800bps。

※PWM 调控：灯光调节，三色LED 调节，电机调速等。

※GPIO控制：控制开关，继电器等。

B.工作模式

ESP8266模块支持STA/AP/STA+AP 三种工作模式。

❊STA
模式：ESP8266模块通过路由器连接互联网，手机或电脑通过互联网实现对设备的远程控制。

![](media/24b76289c7fd3a0a610136e68b1e4a7b.jpeg)

❊AP
模式：ESP8266模块作为热点，实现手机或电脑直接与模块通信，实现局域网无线控制。

❊STA+AP
模式：两种模式的共存模式，即可以通过互联网控制可实现无缝切换，方便操作。![](media/d151bb85f1fd68837fa0594af9e50eb6.jpeg)

C.应用领域

✭✮串口CH340 转Wi-Fi；

✭✮工业透传DTU；

✭✮Wi-Fi 远程监控/控制；

✭✮玩具领域；

✭✮彩色LED 控制；

✭✮消防、安防智能一体化管理；

✭✮智能卡终端，无线POS 机，Wi-Fi 摄像头，手持设备等

4.  安装驱动文件

    这个USB转ESP-01S     WIFI模块串口测试扩展板的USB转串口芯片为CH340，我们需要安装这芯片的驱动，驱动为usb_ch341_3.1.2009.06，我们把该驱动放到D盘（即：复制![](media/a8831dd84a6b22f06776333220de6f54.png)放到D盘），然后开始安装驱动。在不同系统在安装驱动方式大同小异，这里我们在win10系统上开始安装驱动。

1.  当USB转ESP-01S WIFI模块串口测试扩展板第一次接入你的电脑,     右击桌面上的“我的电脑”—\>“属性”—\>“设备管理器”,     即可看到“USB-Serial”。

![](media/34560f6c75cb75df7a79d562830c2a16.png)

2.  点击 “USB-Serial”, 选择“更新驱动程序（P）”。

![](media/f7921e767e8a593ebb01a0520f015442.png)

C.然后点击“浏览计算机以查找驱动程序软件”。

![](media/4515dc55a1c27647f9eaab3fd324053d.png)

D.找到提供的“驱动文件”文件夹。

![](media/5494e57dfc38ef78d0c4ed9676bebec5.png)

5.  安装完成后点击“关闭”。

![](media/b77da8913a0b41b268a48ac9e03a4ce6.png)

6.  驱动安装完成后右键点击“我的电脑”—\>“属性”—\>“设备管理器”,     你可以看到你CH340驱动程序已经成功安装到电脑，如下图。

![](media/65160f9c0aad6caaf94db7ce50ac65e6.png)

5.  将WIFI模块串口测试扩展板插入电脑的USB口：

1.  将ESP8266串口WIFI ESP-01模块正确方向插入USB转ESP-01S     WIFI模块串口测试扩展板上。

    ![](media/91410a61236d00e68e9652b61b4567c3.png)

2.  先将USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关拨到UartDownload端，再将USB转ESP-01S     WIFI模块串口测试扩展板插入电脑的USB口。

    ![](media/c231da919efd2085fac816890fa0fcdd.jpeg)

Arduino搭建Esp8266开发环境

先将ESP8266串口WIFI ESP-01模块正确插入USB转ESP-01S WIFI模块串口测试扩展板中，然后将USB转ESP-01S WIFI模块串口测试扩展板插入电脑的USB口，点击进入arduino-1.8.13文件夹（也可以采用最新版本的），找到![](media/4bb50da9c1d2b944fbee752f56ecb966.png)图标并点击进入1.8.13版本IDE界面。

![](media/e327d18b359e977eb08a4df0f1652e6a.png)

1.  在Arduino IDE里面进行下载安装：

    A.点击文件
    →首选项，在“附加开发板管理器网址：”框中复制粘贴这个地址：http://arduino.esp8266.com/stable/package_esp8266com_index.json，然后点击“好”保存这个地址。

![](media/2070dd1d46f6190084bba232667e8e0e.png)

2.  先点击“工具”→“开发板：”，再点击“开发板管理器”进入“开发板管理器”页面，IDE会自动下载相关文件，如下图。

    ![](media/aab6d0692ba09b54abf746660b696243.png)

3.  相关文件下载成功后，在“全部”后空格中输入“ESP8266”，然后点击下面搜索内容，选择最新版本进行安装，安装包不大，点击“安装”开始安装相关插件。（可能会出现下载安装出错，有可能是服务器原因，需要重新点击“安装”就可以了，但由于网络原因，大多用户可能无法搜到esp8266     by esp8266     Community，对于小白而言不推荐使用此方法添加，推荐下面方法2）

    ![](media/cf82149194b4b17da2f968f9ae835c39.png)

    ![](media/17dbd0e42e2dd533e9aff19138a701e2.png)

4.  安装成功后点击“关闭”关闭页面，然后点击“工具”→“开发板：”，你可以在里面查看到各种不同型号ESP8266开发板。选择对应的ESP8266开发板型号和COM口，选中后即可对ESP8266进行编程。

    ![](media/0bad68997d753796bed0c90363c669fd.png)

    ![](media/6206afde171f5d575019401a5cb1cfa6.png)

2.  通过工具对ESP8266进行安装：（推荐使用这种方法）

使用“esp8266一键安装arduino板_2.5.0版.exe”，一键安装，此方法安装便捷，且安装较快，推荐此方法安装。

![](media/f971a0e820770549a7db3fcdccad59c4.png)

1.  鼠标左键双击“esp8266一键安装arduino板_2.5.0版.exe”，然后就安装完成了。

    ![](media/26132cf19a193d5ef2396a62de5dab89.png)

2.  在上述工具安装完成之后，重启 Arduino IDE 软件，点击 Arduino     菜单栏“工具”→“开发板：” ，可查看到各种不同型号ESP8266开发板。选择对应的ESP8266开发板型号，选中后即可对ESP8266进行编程。

    ![](media/0bad68997d753796bed0c90363c669fd.png)

    ![](media/6206afde171f5d575019401a5cb1cfa6.png)

6.  WIFI 测试代码：

注意：打开IDE后，一定要先设置好板型和COM口。如果家里没有WIFI需要打开手机热点共享WIFI.

```c
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>

#ifndef STASSID
//#define STASSID "your-ssid"
//#define STAPSK  "your-password"
#define STASSID "ChinaNet-2.4G-0DF0"   //用户Wifi名称
#define STAPSK  "ChinaNet@233"         //用户Wifi密码
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

// 端口80的TCP服务器将响应HTTP请求
WiFiServer server(80);

void setup(void) 
{
  Serial.begin(115200);

  // 连接WiFi网络
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // 等待连接
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // 设置mDNS响应器:
  // - 在本例中，第一个参数是域名
  //   完全限定域名为“esp8266.local”
  // - 第二个参数是IP地址
  //   我们通过WiFi网络发送IP地址
  if (!MDNS.begin("esp8266")) 
  {
    Serial.println("Error setting up MDNS responder!");
    while (1) 
    {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");

  // 启动TCP (HTTP)服务器
  server.begin();
  Serial.println("TCP server started");

  // 添加服务器到 MDNS-SD
  MDNS.addService("http", "tcp", 80);
}

void loop(void) 
{

  MDNS.update();

  // 检查客户端是否已连接
  WiFiClient client = server.available();
  if (!client) 
  {
    return;
  }
  Serial.println("");
  Serial.println("New client");

  // 等待来自客户端的数据有效
  while (client.connected() && !client.available()) 
  {
    delay(1);
  }

  // 读取HTTP请求的第一行
  String req = client.readStringUntil('\r');

  // HTTP请求的第一行如下所示 "GET /path HTTP/1.1"
  // Retrieve the "/path" part by finding the spaces
  int addr_start = req.indexOf(' ');
  int addr_end = req.indexOf(' ', addr_start + 1);
  if (addr_start == -1 || addr_end == -1) 
  {
    Serial.print("Invalid request: ");
    Serial.println(req);
    return;
  }
  req = req.substring(addr_start + 1, addr_end);
  Serial.print("Request: ");
  Serial.println(req);
  client.flush();

  String s;
  if (req == "/") 
  {
    IPAddress ip = WiFi.localIP();
    String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
    s += ipStr;
    s += "</html>\r\n\r\n";
    Serial.println("Sending 200");
  } else {
    s = "HTTP/1.1 404 Not Found\r\n\r\n";
    Serial.println("Sending 404");
  }
  client.print(s);
  Serial.println("Done with client");
}
```

7.  项目结果：

    特别注意：需要先将项目代码

![](media/18a5a8dea6bf0248eb84ed745fa8d39c.png)

中的用户Wifi名称和用户Wifi密码改成你们自己的Wifi名称和Wifi密码。

    Wifi名称和Wifi密码修改后，确保USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关已经拨到Uart Download     端，并且也确定USB转ESP-01S     WIFI模块串口测试扩展板已经插入电脑的USB口。然后按照前面方法设置板型和COM口，IDE右下角显示对应板型和COM口，再点击![](media/b1feab597beaa43e4293f7cb6d085551.png)将测试代码上传到ESP8266串口WIFI     ESP-01模块上，上传成功。（注意：如果上传失败，在板型和COM口没问题下，将USB转ESP-01S     WIFI模块串口测试扩展板从电脑的USB口拔下来再次插到电脑的USB口）
    
    ![](media/32e60f07f22496896054c7c4b79047dd.png)
    
    测试代码上传成功后，先将USB转ESP-01S     WIFI模块串口测试扩展板从电脑的USB口拔下来，再将USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关拨到Flash Boot     端，然后再次插到电脑的USB口上。打开串口监视器，设置波特率为115200，即可看到你的WIFI信息，如下图所示：
    
    ![](media/eb07d1bacd23a8bb424a5f50a5e335dc.png)

### 项目30：WIFI 控制LED

1.  项目介绍：

    在前面的项目29中，我们已经知道ESP8266串口WIFI     ESP-01模块通过WIFI测试代码得到相关的WIFI信息。那么在这个项目中，我们将使用ESP8266串口WIFI     ESP-01模块通过APP和WIFI来控制Plus 控制板上的LED点亮和熄灭的效果。

2.  项目元件：

|![](media/de1257ac6af8e184464c71a9326c563a.png)|![](media/db0d16ce23f2cd5383f7188b86315826.png)|![](media/6a11918efdd9458fcbed69415b01934b.png)|
|-|-|-|
|Keyes Uno Plus 控制板*1|USB转ESP-01S WIFI模块串口测试扩展板*1|公对母杜邦线*5|
|![](media/408f9f823aab6078768f08462eda209b.png)|![](media/41abce34fdbca029fdea842bba8208c0.png)![](media/989d26695fd03ea630b7fdf186ff78c1.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|ESP8266串口WIFI ESP-01*1|智能手机/IPad*1|USB 线*1|

3. 将WIFI模块串口测试扩展板插入电脑的USB口：

4. 将ESP8266串口WIFI ESP-01模块正确方向插入USB转ESP-01S     WIFI模块串口测试扩展板上。

   ![](media/91410a61236d00e68e9652b61b4567c3.png)

5. 先将USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关拨到UartDownload端，再将USB转ESP-01S     WIFI模块串口测试扩展板插入电脑的USB口。

   ![](media/c231da919efd2085fac816890fa0fcdd.jpeg)

6. ESP8266 代码：

```c
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>

#ifndef STASSID
#define STASSID "ChinaNet-2.4G-0DF0"  //用户WIFI名称
#define STAPSK  "ChinaNet@233"       //用户WIFI密码
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
WiFiServer server(80);
String unoData = "";
int ip_flag = 0;
int ultra_state = 1;
String ip_str;

void setup() 
{
  Serial.begin(9600); 
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.print("IP ADDRESS: ");
  Serial.println(WiFi.localIP());
  if (!MDNS.begin("esp8266")) 
  {
    //Serial.println("Error setting up MDNS responder!");
    while (1) 
    {
      delay(1000);
    }
  }
 // Serial.println("mDNS responder started");
  server.begin();
  //Serial.println("TCP server started");
  MDNS.addService("http", "tcp", 80);
  ip_flag = 1;
}

void loop() 
{
  if(ip_flag == 1)
  {
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    //Serial.print('#');
    delay(100);
  }
    MDNS.update();
    WiFiClient client = server.available();
    if (!client) 
    {
      return;
    }
    //Serial.println("");
    while (client.connected() && !client.available()) 
    {
      delay(1);
    }
    String req = client.readStringUntil('\r');
    int addr_start = req.indexOf(' ');
    int addr_end = req.indexOf(' ', addr_start + 1);
    if (addr_start == -1 || addr_end == -1) 
    {
      //Serial.print("Invalid request: ");
      //Serial.println(req);
      return;
    }
    req = req.substring(addr_start + 1, addr_end);
    client.flush();
    String s;
    if (req == "/") 
    {
      IPAddress ip = WiFi.localIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
      s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
      s += ipStr;
      s += "</html>\r\n\r\n";
      //Serial.println("Sending 200");
      Serial.println(WiFi.localIP());
      Serial.write('*');
      client.println(WiFi.localIP());
      ip_flag = 0;
    }
    else if(req == "/btn/0")
    {
      Serial.write('a');
      client.println("turn on the relay");
    }
    else if(req == "/btn/1")
    {
      Serial.write('b');
      client.println("turn off the relay");
    }
    else if(req == "/btn/2")
    {
      Serial.write('c');
      client.println("Bring the steering gear over 180 degrees");
    }
    else if(req == "/btn/3")
    {
      Serial.write('d');
      client.println("Bring the steering gear over 0 degrees");
    }
    else if(req == "/btn/4")
    {
      Serial.write('e');
      client.println("esp8266 already turn on the fans");
    }
    else if(req == "/btn/5")
    {
      Serial.write('f');
      client.println("esp8266 already turn off the fans");
    }
    else if(req == "/btn/6")
    {
      Serial.write('g');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
      }
    }
    else if(req == "/btn/7")
    {
      Serial.write('h');
      client.println("turn off the ultrasonic");
    }
    else if(req == "/btn/8")
    {
      Serial.write('i');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/9")
    {
      Serial.write('j');
      client.println("turn off the temperature");
    }
    else if(req == "/btn/10")
    {
      Serial.write('k');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/11")
    {
      Serial.write('l');
      client.println("turn off the humidity");
    }
    else if(req == "/btn/12")
    {
      Serial.write('m');
      client.println(F("m"));
    }
    else if(req == "/btn/13")
    {
      Serial.write('n');
      client.println(F("n"));
    }
    else if(req == "/btn/14")
    {
      Serial.write('o');
      client.println(F("o"));
    }
    else if(req == "/btn/15")
    {
      Serial.write('p');
      client.println(F("p"));
    }
    else if(req == "/btn/16")
    {
      Serial.write('q');
      client.println(F("q"));
    }
    else if(req == "/btn/17")
    {
      Serial.write('r');
      client.println(F("r"));
    }
    else if(req == "/btn/18")
    {
      Serial.write('s');
      client.println(F("s"));
    }
    else if(req == "/btn/19")
    {
      Serial.write('t');
      client.println(F("t"));
    }
    else if(req == "/btn/20")
    {
      Serial.write('u');
      client.println(F("u"));
    }
    else if(req == "/btn/21")
    {
      Serial.write('v');
      client.println(F("v"));
    }
    else if(req == "/btn/22")
    {
      Serial.write('w');
      client.println(F("w"));
    }
    else if(req == "/btn/23")
    {
      Serial.write('x');
      client.println(F("x"));
    }
    else
    {
      //s = "HTTP/1.1 404 Not Found\r\n\r\n";
      //Serial.println("Sending 404");
    }
    client.print(F("IP : "));
    client.println(WiFi.localIP());
}
```

特别注意：需要先将项目代码

![](media/18a5a8dea6bf0248eb84ed745fa8d39c.png)

中的用户Wifi名称和用户Wifi密码改成你们自己的Wifi名称和Wifi密码。

Wifi名称和Wifi密码修改后，确保USB转ESP-01S WIFI模块串口测试扩展板上的拨码开关已经拨到Uart Download
端，并且也确定USB转ESP-01S WIFI模块串口测试扩展板已经插入电脑的USB口。然后按照项目29中的方法设置板型和COM口，IDE右下角显示对应板型和COM口，再点击![](media/b1feab597beaa43e4293f7cb6d085551.png)将测试代码上传到ESP8266串口WIFI ESP-01模块上，上传成功。（注意：如果上传失败，在板型和COM口没问题情况下，将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来再次插到电脑的USB口）

![](media/de14064764c89728503620fe8e32b8af.png)

测试代码上传成功后，先将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来，再将ESP8266串口WIFI ESP-01模块从USB转ESP-01S WIFI模块串口测试扩展板上拔下来。

5.  项目接线：

    ![](media/e902492388e69610ed33df3f8880b02e.png)

6.  项目代码：

注意：打开IDE后，一定要先设置好板型和COM口。如果家里没有WIFI需要打开手机热点共享WIFI

```c
const int ledPin = 13;
char wifiData;

void setup() 
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() 
{
  if(Serial.available() > 0)
  {
    wifiData = Serial.read();
    Serial.print(wifiData);
    if(wifiData == '#')
    {
      Serial.println("");
    }
    delay(100);
    
    if(wifiData == 'a')
    {
      digitalWrite(ledPin, HIGH);
    }
    else if(wifiData == 'b')
    {
      digitalWrite(ledPin, LOW);
    }
  }
}
```



特别注意：上传项目代码前，需要先将连接到Plus控制板上的TX和RX的杜邦线先拔下来，要不然代码上传不成功。然后点击“工具”→“开发板：”，选择Arduino UNO板，选择正确的COM端口，最后再将项目代码上传至Plus控制板。上传代码成功后，再将接在ESP8266串口WIFI ESP-01模块上的TX杜邦线另一端接到Plus控制板上的RX(0)引脚，RX的杜邦线另一端接到Plus控制板上的TX(1)引脚。点击![](media/c26260f4b82d19ca26aeafe9722c59ee.png)打开串口监视器窗口，将波特率设置为9600。这样，串口监视器就显示此时你们WIFI的IP地址。（WIFI的IP地址有时候会改变，如果原来的IP地址不行，需要重新检测WIFI的IP地址）

![](media/bcc1d0938836363cd66f30d33e13e4ad.png)

![](media/691834586a8b18d6c55dbf84c8245e89.png)

7.APP：

安卓系统设备（手机/iPad）APP：

现将文件夹中的keyes wifi.apk文件转移到安卓系统手机或Ipad上，点击keyes wifi.apk文件进入安装页面，点击“安装”按钮，然后再点击“继续安装”按钮，安装完成后点击“打开”按钮就可以进入APP界面。

![](media/a170f013db3bcc20ff4ca281c807aa04.png)

![](media/d620452a9d6188cb3946269510df5ae0.png)
![](media/37a6343d672017cc55da02436b8a82a2.png)

![](media/b2c5bddc60b9c017747a56eea24e412a.png)

![](media/27708677feed5d3acfb062d9222bacda.png)

![](media/c49919d6ee38ca619aaf90aa9b94d4fc.png)

在WIFI按钮前面的文本框中输入检测到的WIFI IP地址（例如，上面串口监视器检测到的IP地址：192.168.1.136），再点击WIFI按钮，“403 Forbidden”或“网页无法打开”就会变成“192.168.1.136”。这样，就说明APP已经连接上了WIFI。

![](media/b06b7fc12ccede5fd0a1b875a1ccfe55.png)

IOS系统设备（手机/iPad）APP

a.打开App Store。

![](media/27924fdb3d67692df7c63d8d0fb72287.png)

b.在搜索框输入keyes wifi，点击搜索，出现下载界面，点击“![](media/962a57f92b78eea1f0e3e81463497a9c.png)”，就可以下载安装keyes wifi的APP。接下来的操作和安卓系统类似的，可以参考上面安卓系统的步骤进行操作。

8. 项目结果：

注意：点击APP上的按钮，ESP8266串口WIFI ESP-01模块上的蓝色指示灯会闪烁，说明APP已经连接上WIFI。

APP已经连接上了WIFI后，开始进行如下操作：

点击APP上的按钮，串口监视器会打印一些对应的控制字符，如下如图所示：

![](media/5b01c36ca8f958a6a3b8883da6d95812.jpeg)

点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，Plus
控制板上的LED点亮，再次点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，Plus
控制板上的LED熄灭。

### 项目31：WIFI的智能家居

1.  项目介绍：

    在前面的项目30中，我们已经知道APP怎样连接上WIFI，并且还用APP通过WIFI控制Plus控制板上LED亮和灭的简单实验。那么在这个项目中，我们将使用APP通过WIFI控制多个传感器/模块工作，实现WIFI智能家居的效果。

2.  项目元件：

|![](media/544243270a027fc8cfa58e0f651a7bf4.png)|![](media/b84cd60ad7a431c5b29c5ae4a36f11f9.jpeg)|![](media/a969001d9732d19eec8b29258dbded2b.png)|![](media/0272e2b8932f9ede6fe4a40359784f72.png)|![](media/1886ee7e1faeea2c093ae626e1b8baaf.png)|
|-|-|-|-|-|
|Keyes Uno Plus 控制板*1|传感器扩展板*1|超声波传感器*1|温湿度传感器*1|舵机*1|
|![](media/34c41dff8cc8c29d81e25c1b01c7b55f.png)|![](media/408f9f823aab6078768f08462eda209b.png)|![](media/bde9eec7397ac2247681093363c9117e.png)|![](media/6a11918efdd9458fcbed69415b01934b.png)|![](media/755ba492c38e44d91e8b2c120dc64904.png)|
|USB转ESP-01S WIFI模块串口测试扩展板*1|ESP8266串口WIFI ESP-01*1|5V继电器模块*1|公对母杜邦线*5|USB 线*1|
|![](media/f9a46e9c7ceca05190317ecfca60c9b5.png) ![](media/3fca19e154e1def9c19033261bae8043.png)|![](media/d0860bd474ce51d4a061fb87348ec82d.jpeg)|![](media/8ea17831247c94b9db6855524e756d16.png)|||
|手机/IPad*1|4P双头连接线*1|3P双头连接线*2|||

3. 将WIFI模块串口测试扩展板插入电脑的USB口：

4. 将ESP8266串口WIFI ESP-01模块正确方向插入USB转ESP-01S     WIFI模块串口测试扩展板上。

   ![](media/91410a61236d00e68e9652b61b4567c3.png)

5. 先将USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关拨到UartDownload端，再将USB转ESP-01S     WIFI模块串口测试扩展板插入电脑的USB口。

   ![](media/c231da919efd2085fac816890fa0fcdd.jpeg)

6. ESP8266 代码：

```c
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>

#ifndef STASSID
#define STASSID "ChinaNet-2.4G-0DF0"  //用户WIFI名称
#define STAPSK  "ChinaNet@233"       //用户WIFI密码
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
WiFiServer server(80);
String unoData = "";
int ip_flag = 0;
int ultra_state = 1;
String ip_str;


void setup() 
{
  Serial.begin(9600); 
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.print("IP ADDRESS: ");
  Serial.println(WiFi.localIP());
  if (!MDNS.begin("esp8266")) 
  {
    //Serial.println("Error setting up MDNS responder!");
    while (1) 
    {
      delay(1000);
    }
  }
 // Serial.println("mDNS responder started");
  server.begin();
  //Serial.println("TCP server started");
  MDNS.addService("http", "tcp", 80);
  ip_flag = 1;
}

void loop() 
{
  if(ip_flag == 1)
  {
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    //Serial.print('#');
    delay(100);
  }
    MDNS.update();
    WiFiClient client = server.available();
    if (!client) 
    {
      return;
    }
    //Serial.println("");
    while (client.connected() && !client.available()) 
    {
      delay(1);
    }
    String req = client.readStringUntil('\r');
    int addr_start = req.indexOf(' ');
    int addr_end = req.indexOf(' ', addr_start + 1);
    if (addr_start == -1 || addr_end == -1) 
    {
      //Serial.print("Invalid request: ");
      //Serial.println(req);
      return;
    }
    req = req.substring(addr_start + 1, addr_end);
    client.flush();
    String s;
    if (req == "/") 
    {
      IPAddress ip = WiFi.localIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
      s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
      s += ipStr;
      s += "</html>\r\n\r\n";
      //Serial.println("Sending 200");
      Serial.println(WiFi.localIP());
      Serial.write('*');
      client.println(WiFi.localIP());
      ip_flag = 0;
    }
    else if(req == "/btn/0")
    {
      Serial.write('a');
      client.println("turn on the relay");
    }
    else if(req == "/btn/1")
    {
      Serial.write('b');
      client.println("turn off the relay");
    }
    else if(req == "/btn/2")
    {
      Serial.write('c');
      client.println("Bring the steering gear over 180 degrees");
    }
    else if(req == "/btn/3")
    {
      Serial.write('d');
      client.println("Bring the steering gear over 0 degrees");
    }
    else if(req == "/btn/4")
    {
      Serial.write('e');
      client.println("esp8266 already turn on the fans");
    }
    else if(req == "/btn/5")
    {
      Serial.write('f');
      client.println("esp8266 already turn off the fans");
    }
    else if(req == "/btn/6")
    {
      Serial.write('g');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
      }
    }
    else if(req == "/btn/7")
    {
      Serial.write('h');
      client.println("turn off the ultrasonic");
    }
    else if(req == "/btn/8")
    {
      Serial.write('i');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/9")
    {
      Serial.write('j');
      client.println("turn off the temperature");
    }
    else if(req == "/btn/10")
    {
      Serial.write('k');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/11")
    {
      Serial.write('l');
      client.println("turn off the humidity");
    }
    else if(req == "/btn/12")
    {
      Serial.write('m');
      client.println(F("m"));
    }
    else if(req == "/btn/13")
    {
      Serial.write('n');
      client.println(F("n"));
    }
    else if(req == "/btn/14")
    {
      Serial.write('o');
      client.println(F("o"));
    }
    else if(req == "/btn/15")
    {
      Serial.write('p');
      client.println(F("p"));
    }
    else if(req == "/btn/16")
    {
      Serial.write('q');
      client.println(F("q"));
    }
    else if(req == "/btn/17")
    {
      Serial.write('r');
      client.println(F("r"));
    }
    else if(req == "/btn/18")
    {
      Serial.write('s');
      client.println(F("s"));
    }
    else if(req == "/btn/19")
    {
      Serial.write('t');
      client.println(F("t"));
    }
    else if(req == "/btn/20")
    {
      Serial.write('u');
      client.println(F("u"));
    }
    else if(req == "/btn/21")
    {
      Serial.write('v');
      client.println(F("v"));
    }
    else if(req == "/btn/22")
    {
      Serial.write('w');
      client.println(F("w"));
    }
    else if(req == "/btn/23")
    {
      Serial.write('x');
      client.println(F("x"));
    }
    else {
      //s = "HTTP/1.1 404 Not Found\r\n\r\n";
      //Serial.println("Sending 404");
    }

    client.print(F("IP : "));
    client.println(WiFi.localIP());
}
```

特别注意：需要先将项目代码

![](media/18a5a8dea6bf0248eb84ed745fa8d39c.png)

中的用户Wifi名称和用户Wifi密码改成你们自己的Wifi名称和Wifi密码。

Wifi名称和Wifi密码修改后，确保USB转ESP-01S WIFI模块串口测试扩展板上的拨码开关已经拨到Uart Download
端，并且也确定USB转ESP-01S WIFI模块串口测试扩展板已经插入电脑的USB口。然后按照项目29中的方法设置板型和COM口，IDE右下角显示对应板型和COM口，再点击![](media/b1feab597beaa43e4293f7cb6d085551.png)将测试代码上传到ESP8266串口WIFI ESP-01模块上，上传成功。（注意：如果上传失败，在板型和COM口没问题情况下，将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来再次插到电脑的USB口）

![](media/b54ea669c4d4550b111300bf341ed980.png)

测试代码上传成功后，先将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来，再将ESP8266串口WIFI ESP-01模块从USB转ESP-01S WIFI模块串口测试扩展板上拔下来。

5.  项目接线：

    ![](media/544afd626f7f345c4f907141cddac0d6.png)

6.  项目代码：

注意：打开IDE后，一定要先设置好板型和COM口。如果家里没有WIFI需要打开手机热点共享WIFI

```c
#include <DHT.h>
DHT dht(2, 11);

#include<Servo.h>
Servo myservo;

char wifiData;
int distance1;
String dis_str;

const int dhtPin = 2;
const int relayPin = 5;
const int trigPin = 12;
const int echoPin = 13;
const int servoPin = 9;

int ip_flag = 1;
int ultra_state = 1;
int temp_state = 1;
int humidity_state = 1;

void setup() 
{
  Serial.begin(9600);
  pinMode(dhtPin, INPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(servoPin, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  digitalWrite(relayPin, LOW); //关闭继电器
  myservo.attach(9);
  dht.begin();
}

void loop() 
{
  if(Serial.available() > 0)
  {
    wifiData = Serial.read();//    Serial.println(wifiData);
    if(wifiData == '*')
    {
      ip_flag = 0;
    }

    if(ip_flag == 1)
    {
      //String ip_addr = Serial.readStringUntil('#');
      Serial.print(wifiData);
      if(wifiData == '#')
      {
        Serial.println("");
      }
      delay(100);
    }
  }

  switch(wifiData)
  {
      case 'a': digitalWrite(relayPin, HIGH); break;
      case 'b': digitalWrite(relayPin, LOW); break;
      case 'c': myservo.write(180); delay(200); break;
      case 'd': myservo.write(0); delay(200); break;
      case 'g': while(ultra_state>0)
      {
           Serial.print("Distance = "); 
           Serial.print(checkdistance());
           Serial.println("#"); 
           ultra_state = 0;
      }
      break;
      case 'h': ultra_state = 1; break;
      case 'i': while(temp_state>0)
      {
         Serial.print("Temperature = "); 
         Serial.print(dht.readTemperature());
         Serial.println("#");
         temp_state = 0;
       }
       break;
      case 'j': temp_state = 1; break;
      case 'k': while(humidity_state > 0)
                {
                  Serial.print("Humidity = "); 
                  Serial.print(dht.readHumidity());
                  Serial.println("#");
                  humidity_state = 0;
                }
                break;
      case 'l': humidity_state = 1; break;
    }
    
}

int checkdistance() {
  digitalWrite(12, LOW);
  delayMicroseconds(2);
  digitalWrite(12, HIGH);
  delayMicroseconds(10);
  digitalWrite(12, LOW);
  int distance = pulseIn(13, HIGH) / 58;
  
  delay(10);
  return distance;
}
```

7.  项目结果：

    特别注意：上传项目代码前，需要先将连接到Plus控制板上的TX和RX的杜邦线先拔下来，要不然代码上传不成功。然后点击“工具”→“开发板：”，选择Arduino     UNO板，选择正确的COM端口，最后再将项目代码上传至Plus控制板。上传代码成功后，再将接在ESP8266串口WIFI     ESP-01模块上的TX杜邦线另一端接到Plus控制板上的RX(0)引脚，RX的杜邦线另一端接到Plus控制板上的TX(1)引脚。点击![](media/c26260f4b82d19ca26aeafe9722c59ee.png)打开串口监视器窗口，将波特率设置为9600。这样，串口监视器就显示此时你们WIFI的IP地址。（WIFI的IP地址有时候会改变，如果原来的IP地址不行，需要重新检测WIFI的IP地址）

    ![](media/3d8fd15694ae516a5e39d82edae05181.png)

    ![](media/3a8ff3eed884af57e7c2673fea427227.png)

    安卓系统设备（手机/iPad）APP：

    现将文件夹中的keyes     wifi.apk文件转移到安卓系统手机或Ipad上，点击keyes     wifi.apk文件进入安装页面，点击“安装”按钮，然后再点击“继续安装”按钮，安装完成后点击“打开”按钮就可以进入APP界面。

    ![](media/a170f013db3bcc20ff4ca281c807aa04.png)

    ![](media/d620452a9d6188cb3946269510df5ae0.png)
    ![](media/37a6343d672017cc55da02436b8a82a2.png)

    ![](media/b2c5bddc60b9c017747a56eea24e412a.png)

    ![](media/27708677feed5d3acfb062d9222bacda.png)

    ![](media/c49919d6ee38ca619aaf90aa9b94d4fc.png)

    在WIFI按钮前面的文本框中输入检测到的WIFI     IP地址（例如，上面串口监视器检测到的IP地址：192.168.1.125），再点击WIFI按钮，“403     Forbidden”或“网页无法打开”就会变成“192.168.1.125”。这样，就说明APP已经连接上了WIFI。

    ![](media/9bfd75cf3e150b0f5b3680144fa55777.png)

IOS系统设备（手机/iPad）APP

a.打开App Store。

![](media/27924fdb3d67692df7c63d8d0fb72287.png)

b.在搜索框输入keyes wifi，点击搜索，出现下载界面，点击“![](media/962a57f92b78eea1f0e3e81463497a9c.png)”，就可以下载安装keyes wifi的APP。接下来的操作和安卓系统类似的，可以参考上面安卓系统的步骤进行操作。

注意：点击APP上的按钮，ESP8266串口WIFI ESP-01模块上的蓝色指示灯会闪烁，说明APP已经连接上WIFI。

APP已经连接上了WIFI后，开始进行如下操作：

1.  点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，继电器打开，APP上显示![](media/505b00b0e23f6498c5d51d5d775c8fcb.png)，模块上的指示灯点亮；再次点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，继电器关闭，APP上显示![](media/deb54a77cdcc87d7569e8b8e46de129f.png)，模块上的指示灯不亮。

2.  点击![](media/c54f78d819d4e6a8310eaeb79ff66910.png)按钮，舵机转动180°，APP上显示![](media/d6feecf1992cbaf09f03c20b5b7f5414.png)；再次点击![](media/c54f78d819d4e6a8310eaeb79ff66910.png)按钮，APP上显示![](media/dee12bee3866542bfe5d70a539f79f0b.png)，舵机转动0°。

3.  点击![](media/95bfbe879d2391e4e48dcae085abe5a6.png)按钮，超声波传感器测距，在超声波传感器前放一个物体，APP上显示![](media/676c8a750e95c84272b0b7791f7b3cd3.png)（不同的距离显示不同的数字），说明此时物体离超声波传感器的距离为14cm，点击![](media/95bfbe879d2391e4e48dcae085abe5a6.png)按钮，关闭超声波，APP上显示![](media/b1df35af68601022e54b7e575b0a07c7.png)。

4.  点击![](media/08c8a35841b31fa4b5327fb7b23a7af5.png)按钮，温湿度传感器测量环境中的温度，APP上显示![](media/2a40f3e895808a3c6d4e1f542133feba.png)，说明此时环境中的温度为28℃；再次点击![](media/08c8a35841b31fa4b5327fb7b23a7af5.png)按钮，关闭温湿度传感器，APP上显示![](media/82887a1385bc7411ecbdc41f60ebd450.png)。

5.  点击![](media/d8e3463ab2f644b3300cdeaa2a68e4c2.png)按钮，温湿度传感器测量环境中的湿度，APP上显示![](media/cc825e69cf073aa73e37712330f4726e.png)，说明此时环境中的湿度为52%；再次点击![](media/d8e3463ab2f644b3300cdeaa2a68e4c2.png)按钮，关闭温湿度传感器，APP上显示![](media/adc18d06e626af067286da9040c20252.png)。
